Skip to content

Commit cf196d9

Browse files
authored
Merge pull request #2 from NJdevPro/extend
Extend
2 parents 84e9c84 + 014f7b3 commit cf196d9

8 files changed

Lines changed: 590 additions & 262 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
*~
2-
minilisp
2+
minilisp
3+
*.o

README.md

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,31 @@ MiniLisp with REPL
22
==================
33

44
Foreword by N. Janin:
5-
This is my attempt at making Rui Ueyama (rui314)'s MiniLisp slightly more user friendly and powerful.
6-
Not being limited by the 1000 lines challenge, I've added a few basic primitives
5+
This is my attempt at making Rui Ueyama (rui314)'s MiniLisp slightly more user friendly
6+
and powerful.
7+
Not being limited by the 1000 lines challenge, I've added a number of basic primitives
78
to the original program, while trying to keep the goal of simplicity and conciseness.
8-
The whole program compiles to less than 100 kb without debugging symbols and should be able to run on low powered devices.
99

10+
The whole program compiles to less than 100 kb without debugging symbols and should be
11+
able to run on low powered devices.
12+
13+
The whole program compiles to less than 100 kb and should be able to run on low powered devices.
1014
The added primitives:
1115
* strings
12-
* operators >, >=, <=, or, and, not,
16+
* predicates >, >=, <=, or, and, not,
17+
1318
* functions length, reverse, progn, load.
14-
This has the side effect of being much faster as well, since all these primitives are compiled
15-
instead of being interpreted.
19+
20+
This has the side effect of being much faster as well, since all these primitives are
21+
compiled instead of being interpreted.
1622

1723
Among the bells and whistles, I've added a REPL based on Justine Tunney (jart)'s bestline.
1824

1925
In this version, instead of passing a file using pipes, you simply pass the files as command parameters :
2026
./minilisp f1 f2 etc
2127

22-
The files all share the same environment, so all the symbols, functions and macros defined in f1 can be reused in the following files.
28+
The files all share the same environment, so all the symbols, functions and macros defined
29+
in f1 can be reused in the following files.
2330

2431
## Shortcuts
2532

@@ -170,12 +177,16 @@ car.
170177
(setcar cell 'x)
171178
cell ; -> (x . b)
172179

173-
`length` and `reverse` operate on a whole list. They can also operate on their arguments.
180+
`length` and `reverse` operate on a whole list or a string. They can also operate on their
181+
arguments when their number is > 1.
182+
183+
(length '(1 2 3)) ; -> 3
184+
(length 1 2 t) ; -> 3
185+
(length "1 2 3") ; -> 5
174186

175-
(length '(1 2 3)) ; -> 3
176-
(length 1 2 t) ; -> 3
177-
(reverse '(a b c)) ; -> (c b a)
178-
(reverse '(a) b c) ; -> (c b (a))
187+
(reverse '(a b c)) ; -> (c b a)
188+
(reverse "1234") ; -> "4321"
189+
(reverse '((a) b "c") ; -> ("c" b (a))
179190

180191
### Numeric operators
181192

@@ -207,7 +218,7 @@ the second.
207218
(< 3 3) ; -> ()
208219
(< 4 3) ; -> ()
209220

210-
The other comparison operators `>`, `<=`, `>=` work in a similar fashion.
221+
The other numerical predicates `>`, `<=`, `>=` work in a similar fashion.
211222

212223
`and` takes two or more arguments, evaluates them, and returns the last argument
213224
that returns true, if all the arguments return true, or () otherwise.
@@ -224,10 +235,10 @@ that returns true.
224235
(or () ()) ; -> ()
225236
(or) ; -> ()
226237

227-
NB: because all the arguments are evaluated, `and` and `or` do not operate like
228-
their counterparts written in Lisp, as those stop evaluation at the first argument
229-
that returns. If the arguments have side effects, this may affect the program
230-
differently.
238+
Nota Bene: because all the arguments are evaluated, `and` and `or` do not operate
239+
like their counterparts written in Lisp, as those stop evaluation at the first
240+
argument that returns. If the arguments have side effects, this may affect the
241+
program differently.
231242

232243
### Conditionals
233244

@@ -247,12 +258,11 @@ exhaustion error.
247258

248259
### Imperative programming
249260

250-
`progn` executes several expressions consecutively.
261+
`(progn expr expr ...)` executes several expressions in sequence.
251262

252-
(progn (print 'I 'own)
253-
(defun add(x y)(+ x y)
254-
(println (add 3 7) 'cents))) ; -> I own
255-
10 cents
263+
(progn (print "I own ")
264+
(defun add(x y)(+ x y))
265+
(println (add 3 7) " cents")) ; -> prints "I own 10 cents"
256266

257267
### Equivalence test operators
258268

@@ -269,7 +279,7 @@ contents but actually different are considered to not be the same by `eq`.
269279

270280
`string-concat` concatenates strings.
271281

272-
(string-concat) ; -> ""
282+
(string-concat) ; -> ""
273283
(string-concat "A" "B" "C" "D") ; -> "ABCD"
274284

275285
`symbol->string` turns a symbol into a string.
@@ -285,8 +295,9 @@ contents but actually different are considered to not be the same by `eq`.
285295

286296
`print` prints a given object to the standard output.
287297

288-
(print 3) ; prints "3"
289-
(print '(hello world)) ; prints "(hello world)"
298+
(print 3) ; -> "3"
299+
(print '(hello world)) ; -> "(hello world)"
300+
(print "hello" "world") ; -> "hello world"
290301

291302
`println` does the same, adding a return at the end.
292303

@@ -345,12 +356,21 @@ is not defined.
345356
(define val (+ 3 5))
346357
(setq val (+ val 1)) ; increment "val"
347358

348-
### system functions
359+
### Introspection
360+
361+
`atom` returns () if the argument is a cell, t otherwise.
362+
363+
(atom '(a b)) ; -> ()
364+
(atom "") ; -> t
365+
(atom ()) ; -> t
366+
367+
### System functions
349368
`load` loads a Lisp file and evaluates all its content, adding it to the environment.
350369

351-
(load 'example/nqueens.lisp) -> run the file and store its evaluated functions and macros
370+
(load "example/nqueens.lisp") -> run the file and store its evaluated functions
371+
and macros
352372

353-
`exit` quits the interpreter and returns integer passed as parameter.
373+
`exit` quits the interpreter and returns the integer passed as parameter.
354374

355375
(exit 0) -> quit with success
356376

examples/hanoi.lisp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
(defun list (x . y) (cons x y))
22

3-
(defun mapc1 (fn xs)
4-
(if (= () xs)
5-
()
6-
(progn
7-
(fn (car xs))
8-
(mapc1 fn (cdr xs)))))
9-
103
(defun hanoi-print (disk from to)
114
(println (string-concat "Move disk " disk
125
" from " (symbol->string from)

src/ketopt.h

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// From https://github.com/attractivechaos/klib
2+
// Under MIT/X11 license.
3+
4+
#ifndef KETOPT_H
5+
#define KETOPT_H
6+
7+
#include <string.h> /* for strchr() and strncmp() */
8+
9+
#define ko_no_argument 0
10+
#define ko_required_argument 1
11+
#define ko_optional_argument 2
12+
13+
typedef struct {
14+
int ind; /* equivalent to optind */
15+
int opt; /* equivalent to optopt */
16+
char *arg; /* equivalent to optarg */
17+
int longidx; /* index of a long option; or -1 if short */
18+
/* private variables not intended for external uses */
19+
int i, pos, n_args;
20+
} ketopt_t;
21+
22+
typedef struct {
23+
char *name;
24+
int has_arg;
25+
int val;
26+
} ko_longopt_t;
27+
28+
ketopt_t KETOPT_INIT = { 1, 0, 0, -1, 1, 0, 0 };
29+
30+
static void ketopt_permute(char *argv[], int j, int n) /* move argv[j] over n elements to the left */
31+
{
32+
int k;
33+
char *p = argv[j];
34+
for (k = 0; k < n; ++k)
35+
argv[j - k] = argv[j - k - 1];
36+
argv[j - k] = p;
37+
}
38+
39+
/**
40+
* Parse command-line options and arguments
41+
*
42+
* This fuction has a similar interface to GNU's getopt_long(). Each call
43+
* parses one option and returns the option name. s->arg points to the option
44+
* argument if present. The function returns -1 when all command-line arguments
45+
* are parsed. In this case, s->ind is the index of the first non-option
46+
* argument.
47+
*
48+
* @param s status; shall be initialized to KETOPT_INIT on the first call
49+
* @param argc length of argv[]
50+
* @param argv list of command-line arguments; argv[0] is ignored
51+
* @param permute non-zero to move options ahead of non-option arguments
52+
* @param ostr option string
53+
* @param longopts long options
54+
*
55+
* @return ASCII for a short option; ko_longopt_t::val for a long option; -1 if
56+
* argv[] is fully processed; '?' for an unknown option or an ambiguous
57+
* long option; ':' if an option argument is missing
58+
*/
59+
int ketopt(ketopt_t *s, int argc, char *argv[], int permute, const char *ostr, const ko_longopt_t *longopts)
60+
{
61+
int opt = -1, i0, j;
62+
if (permute) {
63+
while (s->i < argc && (argv[s->i][0] != '-' || argv[s->i][1] == '\0'))
64+
++s->i, ++s->n_args;
65+
}
66+
s->arg = 0, s->longidx = -1, i0 = s->i;
67+
if (s->i >= argc || argv[s->i][0] != '-' || argv[s->i][1] == '\0') {
68+
s->ind = s->i - s->n_args;
69+
return -1;
70+
}
71+
if (argv[s->i][0] == '-' && argv[s->i][1] == '-') { /* "--" or a long option */
72+
if (argv[s->i][2] == '\0') { /* a bare "--" */
73+
ketopt_permute(argv, s->i, s->n_args);
74+
++s->i, s->ind = s->i - s->n_args;
75+
return -1;
76+
}
77+
s->opt = 0, opt = '?', s->pos = -1;
78+
if (longopts) { /* parse long options */
79+
int k, n_exact = 0, n_partial = 0;
80+
const ko_longopt_t *o = 0, *o_exact = 0, *o_partial = 0;
81+
for (j = 2; argv[s->i][j] != '\0' && argv[s->i][j] != '='; ++j) {} /* find the end of the option name */
82+
for (k = 0; longopts[k].name != 0; ++k)
83+
if (strncmp(&argv[s->i][2], longopts[k].name, j - 2) == 0) {
84+
if (longopts[k].name[j - 2] == 0) ++n_exact, o_exact = &longopts[k];
85+
else ++n_partial, o_partial = &longopts[k];
86+
}
87+
if (n_exact > 1 || (n_exact == 0 && n_partial > 1)) return '?';
88+
o = n_exact == 1? o_exact : n_partial == 1? o_partial : 0;
89+
if (o) {
90+
s->opt = opt = o->val, s->longidx = o - longopts;
91+
if (argv[s->i][j] == '=') s->arg = &argv[s->i][j + 1];
92+
if (o->has_arg == 1 && argv[s->i][j] == '\0') {
93+
if (s->i < argc - 1) s->arg = argv[++s->i];
94+
else opt = ':'; /* missing option argument */
95+
}
96+
}
97+
}
98+
} else { /* a short option */
99+
const char *p;
100+
if (s->pos == 0) s->pos = 1;
101+
opt = s->opt = argv[s->i][s->pos++];
102+
p = strchr((char*)ostr, opt);
103+
if (p == 0) {
104+
opt = '?'; /* unknown option */
105+
} else if (p[1] == ':') {
106+
if (argv[s->i][s->pos] == 0) {
107+
if (s->i < argc - 1) s->arg = argv[++s->i];
108+
else opt = ':'; /* missing option argument */
109+
} else s->arg = &argv[s->i][s->pos];
110+
s->pos = -1;
111+
}
112+
}
113+
if (s->pos < 0 || argv[s->i][s->pos] == 0) {
114+
++s->i, s->pos = 0;
115+
if (s->n_args > 0) /* permute */
116+
for (j = i0; j < s->i; ++j)
117+
ketopt_permute(argv, j, s->n_args);
118+
}
119+
s->ind = s->i - s->n_args;
120+
return opt;
121+
}
122+
123+
#endif

0 commit comments

Comments
 (0)