mirror of
https://github.com/janet-lang/janet
synced 2024-11-19 23:24:49 +00:00
Add boot script which is loaded on start up.
This commit is contained in:
parent
4f74d57359
commit
50bfa8de3f
@ -27,6 +27,18 @@ set (CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
|
||||
|
||||
include_directories(src/include)
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Make tools for generating embeddable headers
|
||||
add_executable(xxd src/tools/xxd.c)
|
||||
|
||||
# Generate header containing standard library
|
||||
add_custom_command(
|
||||
OUTPUT dststlbootstrap.h
|
||||
COMMAND xxd ${CMAKE_CURRENT_SOURCE_DIR}/src/compiler/boot.dst dststlbootstrap.h dst_stl_bootstrap_gen
|
||||
DEPENDS xxd src/compiler/boot.dst
|
||||
COMMENT "Generating stl bootstrap C header for embedding"
|
||||
)
|
||||
|
||||
set(ASSEMBLER_SOURCES
|
||||
src/assembler/asm.c
|
||||
@ -38,6 +50,7 @@ src/compiler/specials.c
|
||||
src/compiler/cfuns.c
|
||||
src/compiler/context.c
|
||||
src/compiler/stl.c
|
||||
dststlbootstrap.h
|
||||
|
||||
src/compiler/compile.h
|
||||
)
|
||||
|
78
src/compiler/boot.dst
Normal file
78
src/compiler/boot.dst
Normal file
@ -0,0 +1,78 @@
|
||||
(def defmacro macro
|
||||
(fn [name & more] (tuple 'def name 'macro (tuple-prepend (tuple-prepend more name) 'fn))))
|
||||
|
||||
(defmacro defn
|
||||
[name & more]
|
||||
(tuple 'def name (tuple-prepend (tuple-prepend more name) 'fn)))
|
||||
|
||||
(defmacro when
|
||||
[cond & body]
|
||||
(tuple 'if cond (tuple-prepend body 'do)))
|
||||
|
||||
(def seq (do
|
||||
(defn array-seq [x]
|
||||
(def len (length x))
|
||||
(var i 0)
|
||||
{
|
||||
:more (fn [] (< i len))
|
||||
:next (fn []
|
||||
(def ret (get x i))
|
||||
(varset! i (+ i 1))
|
||||
ret)
|
||||
})
|
||||
(def seqs {
|
||||
:array array-seq
|
||||
:tuple array-seq
|
||||
:struct (fn [x] x)})
|
||||
(fn [x]
|
||||
(def makeseq (get seqs (type x)))
|
||||
(if makeseq (makeseq x) (error "expected sequence")))))
|
||||
|
||||
(defn range [top]
|
||||
(var i 0)
|
||||
{
|
||||
:more (fn [] (< i top))
|
||||
:next (fn []
|
||||
(def ret i)
|
||||
(varset! i (+ i 1))
|
||||
ret)
|
||||
})
|
||||
|
||||
(defn doseq [s]
|
||||
(def s (seq s))
|
||||
(def more? (get s :more))
|
||||
(def getnext (get s :next))
|
||||
(while (more?)
|
||||
(getnext)))
|
||||
|
||||
(defn map [f s]
|
||||
(def s (seq s))
|
||||
(def more (get s :more))
|
||||
(def getnext (get s :next))
|
||||
{
|
||||
:more more
|
||||
:next (fn [] (f (getnext)))
|
||||
})
|
||||
|
||||
(defn reduce [f start s]
|
||||
(def s (seq s))
|
||||
(def more? (get s :more))
|
||||
(def getnext (get s :next))
|
||||
(var ret start)
|
||||
(while (more?)
|
||||
(varset! ret (f ret (getnext))))
|
||||
ret)
|
||||
|
||||
(defmacro for [head & body]
|
||||
(def head (ast-unwrap head))
|
||||
(def sym (get head 0))
|
||||
(def start (get head 1))
|
||||
(def end (get head 2))
|
||||
(def _inc (get head 3))
|
||||
(def inc (if _inc _inc 1))
|
||||
(tuple 'do
|
||||
(tuple 'var sym start)
|
||||
(tuple 'while (tuple '< sym end)
|
||||
(tuple-prepend body 'do)
|
||||
(tuple 'varset! sym (tuple '+ sym 1))
|
||||
)))
|
@ -45,21 +45,40 @@ static int replread(DstContext *c, enum DstParserStatus status) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cstringread(DstContext *c, enum DstParserStatus status) {
|
||||
char *src = (char *)(c->user);
|
||||
static int bytesread(DstContext *c, enum DstParserStatus status) {
|
||||
const uint8_t *src = (const uint8_t *)(c->user);
|
||||
(void) status;
|
||||
int32_t n = CHUNKSIZE;
|
||||
DstBuffer *b = &c->buffer;
|
||||
if (!b->capacity) {
|
||||
dst_buffer_ensure(b, CHUNKSIZE);
|
||||
}
|
||||
if (!*src) return 1;
|
||||
while (*src && b->count < b->capacity) {
|
||||
dst_buffer_push_u8(b, *src++);
|
||||
}
|
||||
if (!*src) {
|
||||
b->count = 0;
|
||||
if (!c->bufsize) {
|
||||
dst_buffer_push_u8(b, '\n');
|
||||
return 1;
|
||||
}
|
||||
c->user = src;
|
||||
if (c->bufsize < n) n = c->bufsize;
|
||||
dst_buffer_push_bytes(b, src, n);
|
||||
c->bufsize -= n;
|
||||
c->user = (void *)(src + n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Chunk readers */
|
||||
static void filedeinit(DstContext *c) {
|
||||
fclose((FILE *) (c->user));
|
||||
}
|
||||
|
||||
/* Read chunk from file */
|
||||
static int32_t fileread(DstContext *c, enum DstParserStatus status) {
|
||||
size_t nread;
|
||||
FILE *f = (FILE *) c->user;
|
||||
(void) status;
|
||||
c->buffer.count = 0;
|
||||
dst_buffer_ensure(&c->buffer, CHUNKSIZE);
|
||||
nread = fread(c->buffer.data, 1, CHUNKSIZE, f);
|
||||
if (nread != CHUNKSIZE && ferror(f)) {
|
||||
return -1;
|
||||
}
|
||||
c->buffer.count = (int32_t) nread;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -90,23 +109,6 @@ static void simpleerror(DstContext *c, enum DstContextErrorType type, Dst err, s
|
||||
dst_puts(dst_formatc("%s error: %s\n", errtype, dst_to_string(err)));
|
||||
}
|
||||
|
||||
static void filedeinit(DstContext *c) {
|
||||
fclose((FILE *) (c->user));
|
||||
}
|
||||
|
||||
static int fileread(DstContext *c, enum DstParserStatus status) {
|
||||
size_t nread;
|
||||
FILE *f = (FILE *) c->user;
|
||||
(void) status;
|
||||
dst_buffer_ensure(&c->buffer, CHUNKSIZE);
|
||||
nread = fread(c->buffer.data, 1, CHUNKSIZE, f);
|
||||
if (nread != CHUNKSIZE && ferror(f)) {
|
||||
return -1;
|
||||
}
|
||||
c->buffer.count = (int32_t) nread;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dst_context_init(DstContext *c, DstTable *env) {
|
||||
dst_buffer_init(&c->buffer, CHUNKSIZE);
|
||||
c->env = env;
|
||||
@ -147,10 +149,11 @@ int dst_context_file(DstContext *c, DstTable *env, const char *path) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dst_context_cstring(DstContext *c, DstTable *env, const char *source) {
|
||||
int dst_context_bytes(DstContext *c, DstTable *env, const uint8_t *bytes, int32_t len) {
|
||||
dst_context_init(c, env);
|
||||
c->user = (void *) source;
|
||||
c->read_chunk = cstringread;
|
||||
c->user = (void *) bytes;
|
||||
c->bufsize = len;
|
||||
c->read_chunk = bytesread;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -27,13 +27,16 @@
|
||||
#include <dst/dstparse.h>
|
||||
#include <dst/dstcompile.h>
|
||||
|
||||
/* Generated header */
|
||||
#include "dststlbootstrap.h"
|
||||
|
||||
static const DstReg cfuns[] = {
|
||||
{"native", dst_core_native},
|
||||
{"print", dst_core_print},
|
||||
{"describe", dst_core_describe},
|
||||
{"string", dst_core_string},
|
||||
{"symbol", dst_core_symbol},
|
||||
{"buffer-string", dst_core_buffer_to_string},
|
||||
{"buffer", dst_core_buffer},
|
||||
{"table", dst_core_table},
|
||||
{"array", dst_core_array},
|
||||
{"tuple", dst_core_tuple},
|
||||
@ -53,10 +56,7 @@ static const DstReg cfuns[] = {
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static const char *bootstrap =
|
||||
"(def defmacro macro (fn [name & more] (tuple 'def name 'macro (tuple-prepend (tuple-prepend more name) 'fn))))\n"
|
||||
"(defmacro defn [name & more] (tuple 'def name (tuple-prepend (tuple-prepend more name) 'fn)))\n"
|
||||
"(defmacro when [cond & body] (tuple 'if cond (tuple-prepend body 'do)))\n";
|
||||
#include <unistd.h>
|
||||
|
||||
DstTable *dst_stl_env() {
|
||||
static uint32_t error_asm[] = {
|
||||
@ -116,7 +116,7 @@ DstTable *dst_stl_env() {
|
||||
/* Run bootstrap source */
|
||||
{
|
||||
DstContext ctxt;
|
||||
dst_context_cstring(&ctxt, env, bootstrap);
|
||||
dst_context_bytes(&ctxt, env, dst_stl_bootstrap_gen, sizeof(dst_stl_bootstrap_gen));
|
||||
dst_context_run(&ctxt, 0);
|
||||
}
|
||||
|
||||
|
@ -55,8 +55,9 @@ int dst_core_describe(DstArgs args) {
|
||||
for (j = 0; j < len; ++j) {
|
||||
putc(vstr[j], stdout);
|
||||
}
|
||||
putc('\n', stdout);
|
||||
}
|
||||
putc('\n', stdout);
|
||||
if (!i) putc('\n', stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -90,12 +91,16 @@ int dst_core_symbol(DstArgs args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dst_core_buffer_to_string(DstArgs args) {
|
||||
DstBuffer *b;
|
||||
if (args.n != 1) return dst_throw(args, "expected 1 argument");
|
||||
if (!dst_checktype(args.v[0], DST_BUFFER)) return dst_throw(args, "expected buffer");
|
||||
b = dst_unwrap_buffer(args.v[0]);
|
||||
return dst_return(args, dst_wrap_string(dst_string(b->data, b->count)));
|
||||
int dst_core_buffer(DstArgs args) {
|
||||
int32_t i;
|
||||
DstBuffer *b = dst_buffer(0);
|
||||
for (i = 0; i < args.n; ++i) {
|
||||
int32_t len;
|
||||
const uint8_t *str = dst_to_string(args.v[i]);
|
||||
len = dst_string_length(str);
|
||||
dst_buffer_push_bytes(b, str, len);
|
||||
}
|
||||
return dst_return(args, dst_wrap_buffer(b));
|
||||
}
|
||||
|
||||
int dst_core_tuple(DstArgs args) {
|
||||
@ -139,17 +144,6 @@ int dst_core_fiber(DstArgs args) {
|
||||
return dst_return(args, dst_wrap_fiber(fiber));
|
||||
}
|
||||
|
||||
int dst_core_buffer(DstArgs args) {
|
||||
DstBuffer *buffer = dst_buffer(10);
|
||||
int32_t i;
|
||||
for (i = 0; i < args.n; ++i) {
|
||||
const uint8_t *bytes = dst_to_string(args.v[i]);
|
||||
int32_t len = dst_string_length(bytes);
|
||||
dst_buffer_push_bytes(buffer, bytes, len);
|
||||
}
|
||||
return dst_return(args, dst_wrap_buffer(buffer));
|
||||
}
|
||||
|
||||
int dst_core_gensym(DstArgs args) {
|
||||
if (args.n > 1) return dst_throw(args, "expected one argument");
|
||||
if (args.n == 0) {
|
||||
|
@ -45,6 +45,7 @@ struct DstCompileResult {
|
||||
};
|
||||
DstCompileResult dst_compile(Dst source, DstTable *env, int flags);
|
||||
int dst_compile_cfun(DstArgs args);
|
||||
int dst_lib_compile(DstArgs args);
|
||||
|
||||
/* Context - for executing dst in the interpretted form. */
|
||||
typedef struct DstContext DstContext;
|
||||
@ -52,13 +53,17 @@ typedef struct DstContext DstContext;
|
||||
/* Get the default environment for dst */
|
||||
DstTable *dst_stl_env();
|
||||
|
||||
/* Different levels of reporting and configuration */
|
||||
void dst_context_init(DstContext *c, DstTable *env);
|
||||
void dst_context_deinit(DstContext *c);
|
||||
int dst_context_repl(DstContext *c, DstTable *env);
|
||||
int dst_context_file(DstContext *c, DstTable *env, const char *path);
|
||||
int dst_context_cstring(DstContext *c, DstTable *env, const char *source);
|
||||
int dst_context_repl(DstContext *c, DstTable *env);
|
||||
int dst_context_bytes(DstContext *c, DstTable *env, const uint8_t *bytes, int32_t len);
|
||||
|
||||
/* Evaluate a form in the context */
|
||||
int dst_context_run(DstContext *c, int flags);
|
||||
|
||||
void dst_context_deinit(DstContext *c);
|
||||
|
||||
/* Parse structs */
|
||||
enum DstContextErrorType {
|
||||
DST_CONTEXT_ERROR_PARSE,
|
||||
@ -72,6 +77,7 @@ struct DstContext {
|
||||
DstTable *env;
|
||||
DstBuffer buffer;
|
||||
void *user;
|
||||
int32_t bufsize;
|
||||
int32_t index;
|
||||
|
||||
int (*read_chunk)(DstContext *self, enum DstParserStatus status);
|
||||
@ -80,8 +86,6 @@ struct DstContext {
|
||||
void (*deinit)(DstContext *self);
|
||||
};
|
||||
|
||||
int dst_lib_compile(DstArgs args);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -20,8 +20,8 @@
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef DST_MATH_H_defined
|
||||
#define DST_MATH_H_defined
|
||||
#ifndef DST_CORELIB_H_defined
|
||||
#define DST_CORELIB_H_defined
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -29,12 +29,6 @@ extern "C" {
|
||||
|
||||
#include "dsttypes.h"
|
||||
|
||||
/* Basic C Functions. These are good
|
||||
* candidates for optimizations like bytecode
|
||||
* inlining and costant folding. They are exposed publicly
|
||||
* so that compiles can inject them into funcdefs. Later, a
|
||||
* different serialization mechanism might be used for cfunctions. */
|
||||
|
||||
/* Native */
|
||||
int dst_core_native(DstArgs args);
|
||||
|
||||
@ -80,7 +74,7 @@ int dst_core_print(DstArgs args);
|
||||
int dst_core_describe(DstArgs args);
|
||||
int dst_core_string(DstArgs args);
|
||||
int dst_core_symbol(DstArgs args);
|
||||
int dst_core_buffer_to_string(DstArgs args);
|
||||
int dst_core_buffer(DstArgs args);
|
||||
int dst_core_tuple(DstArgs args);
|
||||
int dst_core_array(DstArgs args);
|
||||
int dst_core_table(DstArgs args);
|
||||
@ -113,4 +107,4 @@ Dst dst_op_subtract(Dst lhs, Dst rhs);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DST_MATH_H_defined */
|
||||
#endif /* DST_CORELIB_H_defined */
|
||||
|
@ -62,6 +62,7 @@ int dst_parser_consume(DstParser *parser, uint8_t c);
|
||||
enum DstParserStatus dst_parser_status(DstParser *parser);
|
||||
Dst dst_parser_produce(DstParser *parser);
|
||||
const char *dst_parser_error(DstParser *parser);
|
||||
|
||||
int dst_parse_cfun(DstArgs args);
|
||||
|
||||
int dst_lib_parse(DstArgs args);
|
||||
|
97
src/tools/xxd.c
Normal file
97
src/tools/xxd.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Calvin Rose
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Simple clone of the xxd tool used at build time. Used to
|
||||
* create headers out of source files. Only used for core libraries
|
||||
* like the bootstrapping code and the stl. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define BUFSIZE 1024
|
||||
#define PERLINE 10
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
|
||||
static const char hex[] = "0123456789ABCDEF";
|
||||
char buf[BUFSIZE];
|
||||
size_t bytesRead = 0;
|
||||
int lineIndex = 0;
|
||||
int line = 0;
|
||||
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "Usage: %s infile outfile symbol\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Open the files */
|
||||
FILE *in = fopen(argv[1], "rb");
|
||||
FILE *out = fopen(argv[2], "wb");
|
||||
|
||||
/* Check if files open successfully */
|
||||
if (in == NULL) {
|
||||
fprintf(stderr, "Could not open input file %s\n", argv[1]);
|
||||
return 1;
|
||||
} else if (out == NULL) {
|
||||
fprintf(stderr, "Could not open output file %s\n", argv[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Write the header */
|
||||
fprintf(out, "/* Auto generated - DO NOT EDIT */\n\n");
|
||||
fprintf(out, "static const unsigned char %s[] = {", argv[3]);
|
||||
|
||||
/* Read in chunks from buffer */
|
||||
while ((bytesRead = fread(buf, 1, sizeof(buf), in)) > 0) {
|
||||
size_t i;
|
||||
for (i = 0; i < bytesRead; ++i) {
|
||||
int byte = ((uint8_t *)buf) [i];
|
||||
|
||||
/* Write the byte */
|
||||
if (lineIndex++ == 0) {
|
||||
if (line++)
|
||||
fputc(',', out);
|
||||
fputs("\n\t", out);
|
||||
} else {
|
||||
fputs(", ", out);
|
||||
}
|
||||
fputs("0x", out);
|
||||
fputc(hex[byte >> 4], out);
|
||||
fputc(hex[byte & 0xF], out);
|
||||
|
||||
/* Make line index wrap */
|
||||
if (lineIndex >= PERLINE)
|
||||
lineIndex = 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the tail */
|
||||
fputs("\n};\n\n", out);
|
||||
|
||||
/* Close the file handles */
|
||||
fclose(in);
|
||||
fclose(out);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
# Copyright (c) 2017 Calvin Rose
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
# IN THE SOFTWARE.
|
||||
|
||||
# Bootstrap the language
|
||||
|
||||
(def defmacro macro (fn [name & more]
|
||||
(tuple 'def name 'macro (tuple-prepend (tuple-prepend more name) 'fn))))
|
||||
(defmacro defn
|
||||
[name & more]
|
||||
(tuple 'def name
|
||||
(tuple-prepend (tuple-prepend more name) 'fn)))
|
||||
|
||||
(defmacro when [cond & body] (tuple 'if cond (tuple-prepend body 'do)))
|
||||
|
||||
(defn array-seq [x]
|
||||
(def len (length x))
|
||||
(var i 0)
|
||||
{
|
||||
:more (fn [] (< i len))
|
||||
:next (fn []
|
||||
(def ret (get x i))
|
||||
(varset! i (+ i 1))
|
||||
ret)
|
||||
})
|
||||
|
||||
(def seqs {
|
||||
:array array-seq
|
||||
:tuple array-seq
|
||||
:struct (fn [x] x)})
|
||||
|
||||
(defn seq [x]
|
||||
(def makeseq (get seqs (type x)))
|
||||
(if makeseq (makeseq x) (error "expected sequence")))
|
||||
|
||||
(defn range [top]
|
||||
(var i 0)
|
||||
{
|
||||
:more (fn [] (< i top))
|
||||
:next (fn []
|
||||
(def ret i)
|
||||
(varset! i (+ i 1))
|
||||
i)
|
||||
})
|
||||
|
||||
(defn doseq [s f]
|
||||
(def s (seq s))
|
||||
(def more? (get s :more))
|
||||
(def getnext (get s :next))
|
||||
(while (more?)
|
||||
(f (getnext))))
|
||||
|
||||
(defn map [f s]
|
||||
(def s (seq s))
|
||||
(def more (get s :more))
|
||||
(def getnext (get s :next))
|
||||
{
|
||||
:more more
|
||||
:next (f (getnext))
|
||||
})
|
Loading…
Reference in New Issue
Block a user