mal/mal.mal
miniMAL/mal.json
nim/nimcache*
+objc/*.d
ocaml/*.cmi
ocaml/*.cmo
ocaml/*.swp
--- /dev/null
+FROM ubuntu:vivid
+MAINTAINER Joel Martin <github@martintribe.org>
+
+##########################################################
+# General requirements for testing or common across many
+# implementations
+##########################################################
+
+RUN apt-get -y update
+
+# Required for running tests
+RUN apt-get -y install make python
+
+# Some typical implementation and test requirements
+RUN apt-get -y install curl libreadline-dev libedit-dev
+
+RUN mkdir -p /mal
+WORKDIR /mal
+
+##########################################################
+# Specific implementation requirements
+##########################################################
+
+# Based on:
+# https://blog.tlensing.org/2013/02/24/objective-c-on-linux-setting-up-gnustep-clang-llvm-objective-c-2-0-blocks-runtime-gcd-on-ubuntu-12-04/
+
+RUN apt-get -y install build-essential clang libblocksruntime-dev \
+ libkqueue-dev libpthread-workqueue-dev gobjc libxml2-dev \
+ libjpeg-dev libtiff-dev libpng12-dev libcups2-dev \
+ libfreetype6-dev libcairo2-dev libxt-dev libgl1-mesa-dev
+
+RUN mkdir -p /root/gnustep-dev
+RUN cd /root/gnustep-dev && \
+ curl http://download.gna.org/gnustep/libobjc2-1.7.tar.bz2 \
+ | tar xjf -
+RUN cd /root/gnustep-dev && \
+ curl ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-make-2.6.7.tar.gz \
+ | tar xzf -
+RUN cd /root/gnustep-dev && \
+ curl ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-base-1.24.8.tar.gz \
+ | tar xzf -
+RUN cd /root/gnustep-dev && \
+ curl ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-gui-0.24.1.tar.gz \
+ | tar xzf -
+RUN cd /root/gnustep-dev && \
+ curl ftp://ftp.gnustep.org/pub/gnustep/core/gnustep-back-0.24.1.tar.gz \
+ | tar xzf -
+
+
+# TODO move up
+RUN apt-get -y install gnutls-dev libxslt-dev libffi-dev openssl
+
+ENV CC clang
+RUN cd /root/gnustep-dev/libobjc2-1.7 && make && make install
+RUN cd /root/gnustep-dev/gnustep-make-2.6.7 && ./configure && make && make install
+RUN cd /root/gnustep-dev/gnustep-base-1.24.8 && ./configure && make && make install && ldconfig
+RUN cd /root/gnustep-dev/gnustep-gui-0.24.1 && ./configure && make && make install
+RUN cd /root/gnustep-dev/gnustep-back-0.24.1 && ./configure && make && make install
+
+RUN apt-get -y install libdispatch-dev
+
+ENV HOME /mal
--- /dev/null
+STEP0_DEPS = mal_readline.c mal_readline.h
+STEP1_DEPS = $(STEP0_DEPS) types.h types.m reader.h reader.m printer.h printer.m
+STEP2_DEPS = $(STEP1_DEPS)
+STEP3_DEPS = $(STEP2_DEPS) env.m
+STEP4_DEPS = $(STEP3_DEPS) malfunc.h malfunc.m core.h core.m
+
+SOURCES = $(STEP4_DEPS) stepA_mal.m
+SOURCES_LISP = env.h env.m core.h core.m stepA_mal.m
+
+STEPS = step0_repl step1_read_print step2_eval step3_env \
+ step4_if_fn_do step5_tco step6_file step7_quote \
+ step8_macros step9_try stepA_mal
+
+# From: https://blog.tlensing.org/2013/02/24/objective-c-on-linux-setting-up-gnustep-clang-llvm-objective-c-2-0-blocks-runtime-gcd-on-ubuntu-12-04/:
+# clang `gnustep-config --objc-flags` -o main -x objective-c main.m -fconstant-string-class=NSConstantString -fobjc-nonfragile-abi -fblocks -lgnustep-base -lgnustep-gui -ldispatch -I/usr/local/include/GNUstep -L/usr/local/lib/GNUstep
+
+CC = clang
+LD = ld
+#OBJCC = clang -fblocks -fobjc-nonfragile-abi -fobjc-arc -x objective-c
+OBJCC = clang -fblocks -fobjc-nonfragile-abi -x objective-c
+## Bizzare gnustep-config/make interaction causes make to get run
+## during gnustep-config so we need to remove make output
+OBJC_FLAGS := $(shell gnustep-config --objc-flags | egrep -v "Entering|Leaving")
+OBJC_LIBS := $(filter-out -shared-libgcc,$(shell gnustep-config --base-libs | egrep -v "Entering|Leaving")) -ldispatch -lreadline
+
+all: $(STEPS)
+
+dist: mal
+
+mal: stepA_mal
+ cp $< $@
+
+step0_repl: $(STEP0_DEPS)
+step1_read_print: $(STEP1_DEPS)
+step2_eval: $(STEP2_DEPS)
+step3_env: $(STEP3_DEPS)
+step4_if_fn_do step5_tco step6_file step7_quote step8_macros step9_try stepA_mal: $(STEP4_DEPS)
+
+step%: step%.m
+ $(OBJCC) $(filter-out %.h mal_readline%,$+) -xc mal_readline.c -o $@ $(OBJC_FLAGS) $(OBJC_LIBS)
+
+%.o: %.c
+ $(CC) -c $< -o $@ $(CFLAGS)
+
+%.o: %.m
+ $(OBJCC) -c $< -o $@ $(OBJC_FLAGS)
+
+clean:
+ rm -f $(STEPS) *.o *.d mal
+
+.PHONY: stats tests
+
+stats: $(SOURCES)
+ @wc $^
+ @printf "%5s %5s %5s %s\n" `grep -E "^[[:space:]]*//|^[[:space:]]*$$" $^ | wc` "[comments/blanks]"
+stats-lisp: $(SOURCES_LISP)
+ @wc $^
+ @printf "%5s %5s %5s %s\n" `grep -E "^[[:space:]]*//|^[[:space:]]*$$" $^ | wc` "[comments/blanks]"
--- /dev/null
+#import <Foundation/Foundation.h>
+
+@interface Core : NSObject
+
++ (NSDictionary *)ns;
+
+@end
--- /dev/null
+#import <Foundation/Foundation.h>
+
+#import "types.h"
+#import "printer.h"
+#import "core.h"
+
+NSObject * wrap_tf(BOOL val) {
+ return val ? [MalTrue alloc] : [MalFalse alloc];
+}
+
+@implementation Core
+
++ (NSDictionary *)ns {
+ return @{
+ @"=": ^(NSArray *args){
+ return wrap_tf(equal_Q(args[0], args[1]));
+ },
+
+ @"pr-str": ^(NSArray *args){
+ NSMutableArray * res = [NSMutableArray array];
+ for (id e in args) { [res addObject:_pr_str(e,true)]; }
+ return [res componentsJoinedByString:@" "];
+ },
+ @"str": ^(NSArray *args){
+ NSMutableArray * res = [NSMutableArray array];
+ for (id e in args) { [res addObject:_pr_str(e,false)]; }
+ return [res componentsJoinedByString:@""];
+ },
+ @"prn": ^(NSArray *args){
+ NSMutableArray * res = [NSMutableArray array];
+ for (id e in args) { [res addObject:_pr_str(e,true)]; }
+ printf("%s\n", [[res componentsJoinedByString:@" "] UTF8String]);
+ fflush(stdout);
+ return [NSNull alloc];
+ },
+ @"println": ^(NSArray *args){
+ NSMutableArray * res = [NSMutableArray array];
+ for (id e in args) { [res addObject:_pr_str(e,false)]; }
+ printf("%s\n", [[res componentsJoinedByString:@" "] UTF8String]);
+ fflush(stdout);
+ return [NSNull alloc];
+ },
+
+ @"<": ^(NSArray *args){
+ return wrap_tf([args[0] intValue] < [args[1] intValue]);
+ },
+ @"<=": ^(NSArray *args){
+ return wrap_tf([args[0] intValue] <= [args[1] intValue]);
+ },
+ @">": ^(NSArray *args){
+ return wrap_tf([args[0] intValue] > [args[1] intValue]);
+ },
+ @">=": ^(NSArray *args){
+ return wrap_tf([args[0] intValue] >= [args[1] intValue]);
+ },
+ @"+": ^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] + [args[1] intValue]];
+ },
+ @"-": ^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] - [args[1] intValue]];
+ },
+ @"*": ^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] * [args[1] intValue]];
+ },
+ @"/": ^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] / [args[1] intValue]];
+ },
+
+ @"list": ^(NSArray *args){
+ return args;
+ },
+ @"list?": ^(NSArray *args){
+ return wrap_tf(list_Q(args[0]));
+ },
+
+ @"empty?": ^(NSArray *args){
+ if ([args[0] isKindOfClass:[NSNull class]]) {
+ return wrap_tf(true);
+ } else {
+ return wrap_tf([args[0] count] == 0);
+ }
+ },
+ @"count": ^(NSArray *args){
+ if ([args[0] isKindOfClass:[NSNull class]]) {
+ return @0;
+ } else {
+ return [NSNumber numberWithInt:[args[0] count]];
+ }
+ },
+
+ };
+}
+
+@end
--- /dev/null
+#import <Foundation/Foundation.h>
+
+// See types.h for Env interface definition
--- /dev/null
+#import <Foundation/Foundation.h>
+
+#import "types.h"
+//#import "env.h"
+
+@implementation Env
+
+@synthesize data = _data;
+@synthesize outer = _outer;
+
+- (id)initWithBindings:(Env *)outer binds:(NSArray *)binds exprs:(NSArray *)exprs {
+ self = [super init];
+ if (self) {
+ _outer = outer;
+ _data = [NSMutableDictionary dictionary];
+
+ for (int i=0; i < [binds count]; i++) {
+ if ([(NSString *)binds[i] isEqualTo:@"&"]) {
+ if ([exprs count] > i) {
+ NSRange r = NSMakeRange(i, [exprs count] - i);
+ _data[binds[i+1]] = [exprs subarrayWithRange:r];
+ } else {
+ _data[binds[i+1]] = @[];
+ }
+ break;
+ } else {
+ _data[binds[i]] = exprs[i];
+ }
+ }
+ }
+ return self;
+}
+
+- (id)initWithOuter:(Env *)outer {
+ return [self initWithBindings:outer binds:@[] exprs:@[]];
+}
+
+- (id)init {
+ return [self initWithBindings:nil binds:@[] exprs:@[]];
+}
+
++ (id)fromOuter:(Env *)outer {
+ return [[Env alloc] initWithOuter:outer];
+}
+
++ (id)fromBindings:(Env *)outer binds:(NSArray *)binds exprs:(NSArray *)exprs {
+ return [[Env alloc] initWithBindings:outer binds:binds exprs:exprs];
+}
+
+- (NSObject *) set:(MalSymbol *)key val:(NSObject *)val {
+ _data[key] = val;
+ return val;
+}
+
+- (Env *) find:(MalSymbol *)key {
+ if (_data[key]) {
+ return self;
+ } else if (_outer) {
+ Env * e = _outer;
+ return [e find:key];
+ } else {
+ return nil;
+ }
+}
+
+- (NSObject *) get:(MalSymbol *)key {
+ Env * e = [self find:key];
+ if (e) {
+ return e.data[key];
+ } else {
+ @throw [NSString stringWithFormat:@"'%@' not found", key];
+ }
+}
+
+@end
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#if USE_READLINE
+ #include <readline/readline.h>
+ #include <readline/history.h>
+ #include <readline/tilde.h>
+#else
+ #include <editline/readline.h>
+#endif
+
+int history_loaded = 0;
+
+char HISTORY_FILE[] = "~/.mal-history";
+
+void load_history() {
+ if (history_loaded) { return; }
+ int ret;
+ char *hf = tilde_expand(HISTORY_FILE);
+ if (access(hf, F_OK) != -1) {
+ // TODO: check if file exists first, use non-static path
+#if USE_READLINE
+ ret = read_history(hf);
+#else
+ FILE *fp = fopen(hf, "r");
+ char *line = malloc(80); // getline reallocs as necessary
+ size_t sz = 80;
+ while ((ret = getline(&line, &sz, fp)) > 0) {
+ add_history(line); // Add line to in-memory history
+ }
+ free(line);
+ fclose(fp);
+#endif
+ history_loaded = 1;
+ }
+ free(hf);
+}
+
+void append_to_history() {
+ char *hf = tilde_expand(HISTORY_FILE);
+#ifdef USE_READLINE
+ append_history(1, hf);
+#else
+#if defined(RL_READLINE_VERSION)
+ HIST_ENTRY *he = history_get(history_base+history_length-1);
+#else
+ // libedit-2 segfaults if we add history_base
+ HIST_ENTRY *he = history_get(history_length-1);
+#endif
+ FILE *fp = fopen(hf, "a");
+ if (fp) {
+ fprintf(fp, "%s\n", he->line);
+ fclose(fp);
+ }
+#endif
+ free(hf);
+}
+
+
+// line must be freed by caller
+char *_readline (char prompt[]) {
+ char *line;
+
+ load_history();
+
+ line = readline(prompt);
+ if (!line) return NULL; // EOF
+ add_history(line); // Add input to in-memory history
+
+ append_to_history(); // Flush new line of history to disk
+
+ return line;
+}
+
--- /dev/null
+#ifndef __MAL_READLINE__
+#define __MAL_READLINE__
+
+char *_readline (char prompt[]);
+
+#endif
--- /dev/null
+#import <Foundation/Foundation.h>
+
+/*
+// Forward declaration of Env (see env.h for full interface)
+@class Env;
+*/
+// Forward declaration of EVAL function
+NSObject *EVAL(id ast, id env);
+
+@interface MalFunc : NSObject
+
+@property (copy) NSArray * ast;
+@property (copy) Env * env;
+@property (copy) NSArray * params;
+
+- (id)init:(NSArray *)ast env:(Env *)env params:(NSArray *)params;
+
+- (id)apply:(NSArray *)args;
+
+@end
--- /dev/null
+#import "types.h"
+
+#import "malfunc.h"
+
+@implementation MalFunc
+
+@synthesize ast = _ast;
+@synthesize env = _env;
+@synthesize params = _params;
+
+- (id)init:(NSArray *)ast env:(Env *)env params:(NSArray *)params {
+ self = [super init];
+ if (self) {
+ _ast = ast;
+ _env = env;
+ _params = params;
+ }
+ return self;
+}
+
+- (id)apply:(NSArray *)args {
+ return EVAL(_ast, [Env fromBindings:_env binds:_params exprs:args]);
+}
+
+@end
--- /dev/null
+#import <Foundation/Foundation.h>
+
+NSString * _pr_str(NSObject * obj, BOOL print_readably);
--- /dev/null
+#import <Foundation/Foundation.h>
+
+#import "types.h"
+
+NSString * _pr_str(NSObject * obj, BOOL print_readably) {
+ //NSLog(@"class: %@", [obj class]);
+ if ([obj isMemberOfClass:[NSNull class]]) {
+ return @"nil";
+ } else if ([obj isMemberOfClass:[MalTrue class]]) {
+ return @"true";
+ } else if ([obj isMemberOfClass:[MalFalse class]]) {
+ return @"false";
+ } else if ([obj isKindOfClass:[MalSymbol class]]) {
+ return (NSString *) obj;
+ } else if ([obj isKindOfClass:[NSString class]]) {
+ NSString * str = (NSString *)obj;
+ if ([str length] > 0 && ([str hasPrefix:@"\u029e"])) {
+ return [NSString stringWithFormat:@":%@",
+ [str substringWithRange:NSMakeRange(1, [str length]-1)]];
+ } else if (print_readably) {
+ str = [[[(NSString *)obj
+ stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]
+ stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]
+ stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
+ return [NSString stringWithFormat:@"\"%@\"", str];
+ } else {
+ return str;
+ }
+ } else if ([obj isKindOfClass:[NSArray class]]) {
+ NSMutableArray * elems = [NSMutableArray array];
+ for (NSObject * elem in (NSArray *)obj) {
+ [elems addObject:_pr_str(elem, print_readably)];
+ }
+ if ([obj isKindOfClass:[MalVector class]]) {
+ return [NSString stringWithFormat:@"[%@]",
+ [elems componentsJoinedByString:@" "]];
+ } else {
+ return [NSString stringWithFormat:@"(%@)",
+ [elems componentsJoinedByString:@" "]];
+ }
+ } else if ([obj isKindOfClass:[NSDictionary class]]) {
+ NSDictionary * dict = (NSDictionary *)obj;
+ NSMutableArray * elems = [NSMutableArray array];
+ for (NSString * key in dict) {
+ [elems addObject:_pr_str(key, print_readably)];
+ [elems addObject:_pr_str(dict[key], print_readably)];
+ }
+ return [NSString stringWithFormat:@"{%@}",
+ [elems componentsJoinedByString:@" "]];
+ } else if (block_Q(obj)) {
+ return @"#<native function>";
+ } else {
+ return [obj description];
+ }
+}
--- /dev/null
+NSArray * tokenize(NSString *str);
+NSObject * read_str(NSString *str);
--- /dev/null
+#import <Foundation/Foundation.h>
+
+#import "types.h"
+
+// Only used here, so define interface locally
+@interface Reader : NSObject
+
+- (id)initWithTokens:(NSArray *)toks;
+- (id)init;
+
+- (NSString *) next;
+- (NSString *) peek;
+
+@end
+
+
+@implementation Reader
+
+NSArray *_tokens;
+int _position;
+
+- (id)initWithTokens:(NSArray *)toks {
+ self = [super init];
+ if (self) {
+ _tokens = toks;
+ _position = 0;
+ }
+ return self;
+}
+
+- (id)init {
+ return [self initWithTokens:@[]];
+}
+
+- (NSString *)next {
+ _position++;
+ return _tokens[_position-1];
+}
+
+- (NSString *)peek {
+ if ([_tokens count] > _position) {
+ return _tokens[_position];
+ } else {
+ return nil;
+ }
+}
+
+@end
+
+
+NSArray * tokenize(NSString *str) {
+ NSRegularExpression *regex = [NSRegularExpression
+ regularExpressionWithPattern:@"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:[\\\\].|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}()'\"`@,;]+)"
+ options:0
+ error:NULL];
+
+ NSArray *matches = [regex
+ matchesInString:str
+ options:0
+ range:NSMakeRange(0, [str length])];
+
+ NSMutableArray * tokens = [NSMutableArray array];
+ for (NSTextCheckingResult *match in matches) {
+ [tokens addObject:[str substringWithRange:[match rangeAtIndex:1]]];
+ }
+ return tokens;
+}
+
+NSObject * read_atom(Reader * rdr) {
+ NSRegularExpression *regex = [NSRegularExpression
+ regularExpressionWithPattern:@"(^-?[0-9]+$)|(^-?[0-9][0-9.]*$)|(^nil$)|(^true$)|(^false$)|^\"(.*)\"$|:(.*)|(^[^\"]*$)"
+ options:0
+ error:NULL];
+ NSNumberFormatter *numf = [[NSNumberFormatter alloc] init];
+ numf.numberStyle = NSNumberFormatterDecimalStyle;
+
+ NSString *token = [rdr next];
+
+ NSArray *matches = [regex
+ matchesInString:token
+ options:0
+ range:NSMakeRange(0, [token length])];
+
+ if ([matches count] > 0) {
+ NSTextCheckingResult *match = matches[0];
+ if ([match rangeAtIndex:1].location != -1) { // integer
+ return [numf numberFromString:token];
+ } else if ([match rangeAtIndex:2].location != -1) { // float
+ return [numf numberFromString:token];
+ } else if ([match rangeAtIndex:3].location != -1) { // nil
+ return [NSNull alloc];
+ } else if ([match rangeAtIndex:4].location != -1) { // true
+ return [MalTrue alloc]; // TODO: intern
+ } else if ([match rangeAtIndex:5].location != -1) { // false
+ return [MalFalse alloc]; // TODO: intern
+ } else if ([match rangeAtIndex:6].location != -1) { // string
+ NSString * str = [token substringWithRange:[match rangeAtIndex:6]];
+ return [[[str
+ stringByReplacingOccurrencesOfString:@"\\\"" withString:@"\""]
+ stringByReplacingOccurrencesOfString:@"\\n" withString:@"\n"]
+ stringByReplacingOccurrencesOfString:@"\\\\" withString:@"\\"];
+ return [token substringWithRange:[match rangeAtIndex:6]];
+ } else if ([match rangeAtIndex:7].location != -1) { // keyword
+ return [NSString stringWithFormat:@"\u029e%@",
+ [token substringWithRange:[match rangeAtIndex:7]]];
+ } else if ([match rangeAtIndex:8].location != -1) { // symbol
+ return [MalSymbol stringWithString:token];
+ }
+ }
+
+ return @0;
+}
+
+// Only used locally, so declare here
+NSObject * read_form(Reader * rdr);
+
+NSArray * read_list(Reader * rdr, char start, char end) {
+ NSString * token = [rdr next];
+ NSMutableArray * ast = [NSMutableArray array];
+
+ if ([token characterAtIndex:0] != start) {
+ @throw [NSString stringWithFormat:@"expected '%c'", start];
+ }
+ while ((token = [rdr peek]) && ([token characterAtIndex:0] != end)) {
+ [ast addObject:read_form(rdr)];
+ }
+ if (!token) {
+ @throw [NSString stringWithFormat:@"expected '%c', got EOF", end];
+ }
+ [rdr next];
+ return ast;
+}
+
+NSObject * read_form(Reader * rdr) {
+ NSString *token = [rdr peek];
+ switch ([token characterAtIndex:0]) {
+ case '\'': [rdr next];
+ return @[[MalSymbol stringWithString:@"quote"],
+ read_form(rdr)];
+ case '`': [rdr next];
+ return @[[MalSymbol stringWithString:@"quasiquote"],
+ read_form(rdr)];
+ case '~': [rdr next];
+ if ([token isEqualToString:@"~@"]) {
+ return @[[MalSymbol stringWithString:@"splice-unquote"],
+ read_form(rdr)];
+ } else {
+ return @[[MalSymbol stringWithString:@"unquote"],
+ read_form(rdr)];
+ }
+ case '^': [rdr next];
+ NSObject * meta = read_form(rdr);
+ return @[[MalSymbol stringWithString:@"with-meta"],
+ read_form(rdr),
+ meta];
+ case '@': [rdr next];
+ return @[[MalSymbol stringWithString:@"deref"],
+ read_form(rdr)];
+
+ // lists
+ case ')':
+ @throw @"unexpected ')'";
+ case '(':
+ return read_list(rdr, '(', ')');
+
+ // vectors
+ case ']':
+ @throw @"unexpected ']'";
+ case '[':
+ return [MalVector fromArray:read_list(rdr, '[', ']')];
+
+ // hash maps
+ case '}':
+ @throw @"unexpected '}'";
+ case '{':
+ return hash_map(read_list(rdr, '{', '}'));
+ default:
+ return read_atom(rdr);
+ }
+}
+
+NSObject * read_str(NSString *str) {
+ NSArray * tokens = tokenize(str);
+ return read_form([[Reader alloc] initWithTokens:tokens]);
+}
--- /dev/null
+#import <Foundation/Foundation.h>
+
+#import "mal_readline.h"
+
+NSString *READ(NSString *str) {
+ return str;
+}
+
+NSString *EVAL(NSString *ast, NSString *env) {
+ return ast;
+}
+
+NSString *PRINT(NSString *exp) {
+ return exp;
+}
+
+NSString *REP(NSString *line) {
+ return PRINT(EVAL(READ(line), @""));
+}
+
+int main (int argc, const char * argv[]) {
+ // Create an autorelease pool to manage the memory into the program
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ // If using automatic reference counting (ARC), use @autoreleasepool instead:
+// @autoreleasepool {
+
+ while (true) {
+ char *rawline = _readline("user> ");
+ if (!rawline) { break; }
+ NSString *line = [NSString stringWithUTF8String:rawline];
+ if ([line length] == 0) { continue; }
+ printf("%s\n", [[REP(line) description] UTF8String]);
+ }
+
+ [pool drain];
+
+// }
+}
--- /dev/null
+#import <Foundation/Foundation.h>
+
+#import "mal_readline.h"
+#import "reader.h"
+#import "printer.h"
+
+NSObject *READ(NSString *str) {
+ return read_str(str);
+}
+
+NSObject *EVAL(NSObject *ast, NSString *env) {
+ return ast;
+}
+
+NSString *PRINT(NSObject *exp) {
+ return _pr_str(exp, true);
+}
+
+NSString *REP(NSString *line) {
+ return PRINT(EVAL(READ(line), @""));
+}
+
+int main (int argc, const char * argv[]) {
+ // Create an autorelease pool to manage the memory into the program
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ // If using automatic reference counting (ARC), use @autoreleasepool instead:
+// @autoreleasepool {
+
+ while (true) {
+ char *rawline = _readline("user> ");
+ if (!rawline) { break; }
+ NSString *line = [NSString stringWithUTF8String:rawline];
+ if ([line length] == 0) { continue; }
+ @try {
+ printf("%s\n", [[REP(line) description] UTF8String]);
+ } @catch(NSString *e) {
+ printf("Error: %s\n", [e UTF8String]);
+ }
+ }
+
+ [pool drain];
+
+// }
+}
--- /dev/null
+#import <Foundation/Foundation.h>
+
+#import "mal_readline.h"
+#import "types.h"
+#import "reader.h"
+#import "printer.h"
+
+// read
+NSObject *READ(NSString *str) {
+ return read_str(str);
+}
+
+// eval
+
+// forward declaration
+NSObject *EVAL(NSObject *ast, NSDictionary *env);
+
+NSObject *eval_ast(NSObject *ast, NSDictionary *env) {
+ if ([ast isMemberOfClass:[MalSymbol class]]) {
+ if ([env objectForKey:ast]) {
+ return env[ast];
+ } else {
+ @throw [NSString stringWithFormat:@"'%@' not found", ast];
+ }
+ } else if ([ast isKindOfClass:[NSArray class]]) {
+ NSMutableArray *newLst = [NSMutableArray array];
+ for (NSObject * x in (NSArray *)ast) {
+ [newLst addObject:EVAL(x, env)];
+ }
+ if ([ast isKindOfClass:[MalVector class]]) {
+ return [MalVector fromArray:newLst];
+ } else {
+ return newLst;
+ }
+ } else if ([ast isKindOfClass:[NSDictionary class]]) {
+ NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
+ for (NSString * k in (NSDictionary *)ast) {
+ newDict[k] = EVAL(((NSDictionary *)ast)[k], env);
+ }
+ return newDict;
+ } else {
+ return ast;
+ }
+}
+
+NSObject *EVAL(NSObject *ast, NSDictionary *env) {
+ //NSLog(@"EVAL: %@", ast);
+ if (!list_Q(ast)) {
+ return eval_ast(ast, env);
+ }
+
+ NSArray * el = (NSArray *) eval_ast(ast, env);
+ NSObject * (^ f)(NSArray *) = el[0];
+ NSArray * args = [el subarrayWithRange:NSMakeRange(1, [el count] - 1)];
+ return f(args);
+}
+
+// print
+NSString *PRINT(NSObject *exp) {
+ return _pr_str(exp, true);
+}
+
+// REPL
+NSString *REP(NSString *line, NSDictionary *env) {
+ return PRINT(EVAL(READ(line), env));
+}
+
+int main (int argc, const char * argv[]) {
+ // Create an autorelease pool to manage the memory into the program
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ // If using automatic reference counting (ARC), use @autoreleasepool instead:
+// @autoreleasepool {
+
+ NSDictionary * repl_env = @{
+ @"+": ^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] + [args[1] intValue]];
+ },
+ @"-": ^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] - [args[1] intValue]];
+ },
+ @"*": ^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] * [args[1] intValue]];
+ },
+ @"/": ^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] / [args[1] intValue]];
+ },
+ };
+
+ while (true) {
+ char *rawline = _readline("user> ");
+ if (!rawline) { break; }
+ NSString *line = [NSString stringWithUTF8String:rawline];
+ if ([line length] == 0) { continue; }
+ @try {
+ printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
+ } @catch(NSString *e) {
+ printf("Error: %s\n", [e UTF8String]);
+ }
+ }
+
+ [pool drain];
+
+// }
+}
--- /dev/null
+#import <Foundation/Foundation.h>
+
+#import "mal_readline.h"
+#import "types.h"
+#import "reader.h"
+#import "printer.h"
+#import "env.h"
+
+// read
+NSObject *READ(NSString *str) {
+ return read_str(str);
+}
+
+// eval
+
+// forward declaration
+NSObject *EVAL(NSObject *ast, Env *env);
+
+NSObject *eval_ast(NSObject *ast, Env *env) {
+ if ([ast isMemberOfClass:[MalSymbol class]]) {
+ return [env get:(MalSymbol *)ast];
+ } else if ([ast isKindOfClass:[NSArray class]]) {
+ NSMutableArray *newLst = [NSMutableArray array];
+ for (NSObject * x in (NSArray *)ast) {
+ [newLst addObject:EVAL(x, env)];
+ }
+ if ([ast isKindOfClass:[MalVector class]]) {
+ return [MalVector fromArray:newLst];
+ } else {
+ return newLst;
+ }
+ } else if ([ast isKindOfClass:[NSDictionary class]]) {
+ NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
+ for (NSString * k in (NSDictionary *)ast) {
+ newDict[k] = EVAL(((NSDictionary *)ast)[k], env);
+ }
+ return newDict;
+ } else {
+ return ast;
+ }
+}
+
+NSObject *EVAL(NSObject *ast, Env *env) {
+ //NSLog(@"EVAL: %@", ast);
+ if (!list_Q(ast)) {
+ return eval_ast(ast, env);
+ }
+
+ NSArray * alst = (NSArray *)ast;
+ id a0 = alst[0];
+ if (![a0 isKindOfClass:[MalSymbol class]]) {
+ @throw @"attempt to apply on non-symbol";
+ }
+ if ([(NSString *)a0 isEqualTo:@"def!"]) {
+ return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];
+ } else if ([(NSString *)a0 isEqualTo:@"let*"]) {
+ Env *let_env = [Env fromOuter:env];
+ NSArray * binds = (NSArray *)alst[1];
+ for (int i=0; i < [binds count]; i+=2) {
+ [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];
+ }
+ return EVAL(alst[2], let_env);
+ } else {
+ NSArray * el = (NSArray *) eval_ast(ast, env);
+ NSObject * (^ f)(NSArray *) = el[0];
+ NSArray * args = [el subarrayWithRange:NSMakeRange(1, [el count] - 1)];
+ return f(args);
+ }
+}
+
+// print
+NSString *PRINT(NSObject *exp) {
+ return _pr_str(exp, true);
+}
+
+// REPL
+NSString *REP(NSString *line, Env *env) {
+ return PRINT(EVAL(READ(line), env));
+}
+
+int main (int argc, const char * argv[]) {
+ // Create an autorelease pool to manage the memory into the program
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ // If using automatic reference counting (ARC), use @autoreleasepool instead:
+// @autoreleasepool {
+
+ Env * repl_env = [[Env alloc] init];
+ [repl_env set:(MalSymbol *)@"+" val:^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] + [args[1] intValue]];
+ }];
+ [repl_env set:(MalSymbol *)@"-" val:^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] - [args[1] intValue]];
+ }];
+ [repl_env set:(MalSymbol *)@"*" val:^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] * [args[1] intValue]];
+ }];
+ [repl_env set:(MalSymbol *)@"/" val:^(NSArray *args){
+ return [NSNumber numberWithInt:[args[0] intValue] / [args[1] intValue]];
+ }];
+
+ while (true) {
+ char *rawline = _readline("user> ");
+ if (!rawline) { break; }
+ NSString *line = [NSString stringWithUTF8String:rawline];
+ if ([line length] == 0) { continue; }
+ @try {
+ printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
+ } @catch(NSString *e) {
+ printf("Error: %s\n", [e UTF8String]);
+ }
+ }
+
+ [pool drain];
+
+// }
+}
--- /dev/null
+#import <Foundation/Foundation.h>
+
+#import "mal_readline.h"
+#import "types.h"
+#import "reader.h"
+#import "printer.h"
+#import "env.h"
+#import "malfunc.h"
+#import "core.h"
+
+// read
+NSObject *READ(NSString *str) {
+ return read_str(str);
+}
+
+// eval
+
+// forward declaration
+NSObject *EVAL(NSObject *ast, Env *env);
+
+NSObject *eval_ast(NSObject *ast, Env *env) {
+ if ([ast isMemberOfClass:[MalSymbol class]]) {
+ return [env get:(MalSymbol *)ast];
+ } else if ([ast isKindOfClass:[NSArray class]]) {
+ NSMutableArray *newLst = [NSMutableArray array];
+ for (NSObject * x in (NSArray *)ast) {
+ [newLst addObject:EVAL(x, env)];
+ }
+ if ([ast isKindOfClass:[MalVector class]]) {
+ return [MalVector fromArray:newLst];
+ } else {
+ return newLst;
+ }
+ } else if ([ast isKindOfClass:[NSDictionary class]]) {
+ NSMutableDictionary *newDict = [NSMutableDictionary dictionary];
+ for (NSString * k in (NSDictionary *)ast) {
+ newDict[k] = EVAL(((NSDictionary *)ast)[k], env);
+ }
+ return newDict;
+ } else {
+ return ast;
+ }
+}
+
+NSObject *EVAL(NSObject *ast, Env *env) {
+ //NSLog(@"EVAL: %@ (%@)", _pr_str(ast, true), env);
+ if (!list_Q(ast)) {
+ return eval_ast(ast, env);
+ }
+
+ NSArray * alst = (NSArray *)ast;
+ id a0 = alst[0];
+ NSString * a0sym = [a0 isKindOfClass:[MalSymbol class]] ? (NSString *)a0
+ : @"__<*fn*>__";
+
+ if ([a0sym isEqualTo:@"def!"]) {
+ return [env set:((MalSymbol *)alst[1]) val:EVAL(alst[2], env)];
+ } else if ([(NSString *)a0 isEqualTo:@"let*"]) {
+ Env *let_env = [Env fromOuter:env];
+ NSArray * binds = (NSArray *)alst[1];
+ for (int i=0; i < [binds count]; i+=2) {
+ [let_env set:binds[i] val:EVAL(binds[i+1], let_env)];
+ }
+ return EVAL(alst[2], let_env);
+ } else if ([a0sym isEqualTo:@"do"]) {
+ NSRange r = NSMakeRange(1, [alst count] - 1);
+ NSArray * el = (NSArray *)eval_ast([alst subarrayWithRange:r], env);
+ return [el lastObject];
+ } else if ([a0sym isEqualTo:@"if"]) {
+ NSObject * cond = EVAL(alst[1], env);
+ if ([cond isKindOfClass:[NSNull class]] ||
+ [cond isKindOfClass:[MalFalse class]]) {
+ if ([alst count] > 3) {
+ return EVAL(alst[3], env);
+ } else {
+ return [NSNull alloc];
+ }
+ } else {
+ return EVAL(alst[2], env);
+ }
+ } else if ([a0sym isEqualTo:@"fn*"]) {
+ return [[MalFunc alloc] init:alst[2] env:env params:alst[1]];
+ } else {
+ NSArray * el = (NSArray *) eval_ast(ast, env);
+ NSArray * args = @[];
+ if ([el count] > 1) {
+ args = [el subarrayWithRange:NSMakeRange(1, [el count] - 1)];
+ }
+ if ([el[0] isKindOfClass:[MalFunc class]]) {
+ MalFunc * mf = el[0];
+ return [mf apply:args];
+ } else {
+ NSObject * (^ f)(NSArray *) = el[0];
+ return f(args);
+ }
+ }
+}
+
+// print
+NSString *PRINT(NSObject *exp) {
+ return _pr_str(exp, true);
+}
+
+// REPL
+NSString *REP(NSString *line, Env *env) {
+ return PRINT(EVAL(READ(line), env));
+}
+
+int main (int argc, const char * argv[]) {
+ // Create an autorelease pool to manage the memory into the program
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+ // If using automatic reference counting (ARC), use @autoreleasepool instead:
+// @autoreleasepool {
+
+ // core.m: defined using Objective-C
+ Env * repl_env = [[Env alloc] init];
+ NSDictionary * core_ns = [Core ns];
+ for (NSString* key in core_ns) {
+ [repl_env set:(MalSymbol *)key val:[core_ns objectForKey:key]];
+ }
+
+ // core.mal: defined using the language itself
+ REP(@"(def! not (fn* (a) (if a false true)))", repl_env);
+
+ while (true) {
+ char *rawline = _readline("user> ");
+ if (!rawline) { break; }
+ NSString *line = [NSString stringWithUTF8String:rawline];
+ if ([line length] == 0) { continue; }
+ @try {
+ printf("%s\n", [[REP(line, repl_env) description] UTF8String]);
+ } @catch(NSString *e) {
+ printf("Error: %s\n", [e UTF8String]);
+ }
+ }
+
+ [pool drain];
+
+// }
+}
--- /dev/null
+#import <Foundation/Foundation.h>
+
+//
+// Env definition
+//
+
+@class MalSymbol;
+
+@interface Env : NSObject
+
+@property (copy) NSMutableDictionary * data;
+@property (copy) Env * outer;
+
+- (id)initWithBindings:(Env *)outer binds:(NSArray *)binds exprs:(NSArray *)exprs;
+- (id)initWithOuter:(Env *)outer;
+- (id)init;
+
++ (id)fromOuter:(Env *)outer;
++ (id)fromBindings:(Env *)outer binds:(NSArray *)binds exprs:(NSArray *)exprs;
+
+- (NSObject *) set:(MalSymbol *)key val:(NSObject *)val;
+- (Env *) find:(MalSymbol *)key;
+- (NSObject *) get:(MalSymbol *)key;
+
+@end
+
+//
+// Mal Types
+//
+
+
+@interface MalTrue : NSObject
+@end
+
+@interface MalFalse : NSObject
+@end
+
+@interface MalSymbol: NSString
+@end
+
+
+// Lists
+
+BOOL list_Q(id obj);
+
+
+// Vectors
+
+@interface MalVector : NSArray
+
+@property (copy) NSArray * array;
+@property(readonly) NSUInteger count;
+
+- (id)initWithArray:(NSArray *)arr;
+- (id)init;
+
++ (id)fromArray:(NSArray *)arr;
+
+- (id)objectAtIndex:(NSUInteger)index;
+
+@end
+
+
+// Hash Maps
+
+NSDictionary * hash_map(NSArray *kvs);
+
+
+// Mal Functions
+
+BOOL block_Q(id obj);
+
+
+
+// General functions
+
+BOOL equal_Q(NSObject * a, NSObject * b);
--- /dev/null
+#import "types.h"
+
+@implementation MalTrue
+@end
+
+@implementation MalFalse
+@end
+
+
+// NSString subclassing based on:
+// http://stackoverflow.com/a/21331422/471795
+
+// Symbols
+
+@interface MalSymbol ()
+@property (nonatomic, strong) NSString *stringHolder;
+@end
+
+@implementation MalSymbol
+
+- (instancetype)initWithCharactersNoCopy:(unichar *)characters length:(NSUInteger)length freeWhenDone:(BOOL)freeBuffer {
+ self = [super init];
+ if (self) {
+ self.stringHolder = [[NSString alloc] initWithCharactersNoCopy:characters length:length freeWhenDone:freeBuffer];
+ }
+ return self;
+}
+
+- (NSUInteger)length {
+ return self.stringHolder.length;
+}
+
+- (unichar)characterAtIndex:(NSUInteger)index {
+ return [self.stringHolder characterAtIndex:index];
+}
+
+@end
+
+
+// Lists
+
+//// Add map to NSArray
+//@implementation NSArray (CMMap)
+//- (NSArray *) map:(NSObject *(^)(NSObject * obj))block {
+// NSMutableArray *res = [NSMutableArray array];
+// for (NSObject * x in self) {
+// [res addObject: block(x)];
+// }
+// return res;
+//}
+//@end
+//
+// E.g.:
+// return [(NSArray *)ast map:^(NSObject * x) { return EVAL(x, env); }];
+
+BOOL list_Q(id obj) {
+ return ([obj isKindOfClass:[NSArray class]] &&
+ ![obj isKindOfClass:[MalVector class]]);
+}
+
+// Vectors
+
+@implementation MalVector
+
+@synthesize array = _array;
+@synthesize count = _count;
+
+- (id)initWithArray:(NSArray *)arr {
+ self = [super init];
+ if (self) {
+ _array = arr;
+ _count = [arr count];
+ }
+ return self;
+}
+
+- (id)init {
+ return [self initWithArray:@[]];
+}
+
++ (id)fromArray:(NSArray *)arr {
+ return [[MalVector alloc] initWithArray:arr];
+}
+
+- (id)objectAtIndex:(NSUInteger)index {
+ return _array[index];
+}
+
+@end
+
+
+// Hash Maps
+
+NSDictionary * assoc_BANG(NSMutableDictionary * d, NSArray * kvs) {
+ for (int i=0; i < [kvs count]; i+=2) {
+ d[kvs[i]] = kvs[i+1];
+ }
+ return d;
+}
+
+NSDictionary * hash_map(NSArray *kvs) {
+ return assoc_BANG([NSMutableDictionary dictionary], kvs);
+}
+
+
+// Mal Functions
+
+BOOL block_Q(id obj) {
+ id block = ^{};
+ Class blockClass = [block class];
+ while ([blockClass superclass] != [NSObject class]) {
+ blockClass = [blockClass superclass];
+ }
+ return [obj isKindOfClass:blockClass];
+}
+
+
+// General functions
+
+BOOL sequential_Q(NSObject * obj) {
+ return [obj isKindOfClass:[NSArray class]];
+}
+
+BOOL equal_Q(NSObject * a, NSObject * b) {
+ //NSLog(@"= %@ (%@), %@ (%@)", a, [a class], b, [b class]);
+ if (!(([a class] == [b class]) ||
+ ([a isKindOfClass:[NSArray class]] &&
+ [b isKindOfClass:[NSArray class]]) ||
+ ([a isKindOfClass:[NSNumber class]] &&
+ [b isKindOfClass:[NSNumber class]]))) {
+ return false;
+ }
+ if ([a isKindOfClass:[MalTrue class]]) {
+ return true;
+ } else if ([a isKindOfClass:[MalFalse class]]) {
+ return true;
+ } else if ([a isKindOfClass:[NSNumber class]]) {
+ return [(NSNumber *)a intValue] == [(NSNumber *)b intValue];
+ } else {
+ return [a isEqual:b];
+ }
+}