-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbf.l
More file actions
106 lines (92 loc) · 3.75 KB
/
Copy pathbf.l
File metadata and controls
106 lines (92 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#########
# Runtime
#########
# The cells are represented as
# 1. `cur`, the value of the current cell
# 2. `l`, the cells to the left of the pointer (a stack, top is closest)
# 3. `r`, the cells to the right of the pointer (a stack, top is closest)
#
# The tape is moved by pushing the current value onto `l` or `r`, then popping
# the top of `r` or `l` into `cur`, defaulting to `0` if the list just popped
# is empty.
(setq cur 0) # the value at the current cell
(de bf-right () # move the tape right
(push 'l cur)
(setq cur (or (pop 'r) 0))) # popping an empty list does nothing and returns `NIL`
(de bf-left () # move the tape left
(push 'r cur)
(setq cur (or (pop 'l) 0)))
(de bf-add (n) # add number to the current cell
(setq cur (+ cur n)))
(de bf-out () # print current cell
(prin (char cur))) # `(char cur)` is the character represented by the number `cur`
(de bf-in () # read current cell
(setq cur (char (char)))) # `(char)` reads one character
# If a function's argument list is given as a symbol without parentheses, then
# when the function is encountered, the arguments are bound to the symbol
# WITHOUT EVALUATING THEM FIRST. In this instance, `bf-loop` is encountered
# like this:
# `(bf-loop (bf-inc 2) (bf-print))`
# The definition of a typical function would read `(de regular (a b c) ...)`
# and the invocation `(regular 1 2 (+ 3 4))` would evaluate each argument, then
# bind `1` to `a`, `2` to `b`, and `7` to `c`. But the definition of `bf-loop`,
# which reads `(de bf-loop a ...)`, together with the invocation
# `(bf-loop 1 2 (+ 3 4))`, binds the LIST `(1 2 (+ 3 4))` to `a`. In other
# words, `a` is a list with three elements, and the third element is itself a
# list with three elements. This allows `bf-loop` to treat its arguments as a
# sequence of instructions, which can be executed with `(run)` or `(eval)`.
#
# This feature of PicoLisp --- a way to prevent argument lists from being
# evaluated --- is incredibly flexible and powerful and essentially obviates
# the need for macros.
(de bf-loop a # run `a` while `cur` is not `0`
(while (n0 cur)
(run a)))
########
# Parser
########
# The preferred method of making a list is `(make ...)`. `make` evaluates its
# arguments sequentially (just like `prog`), but also starts building a list.
# `link` adds # its argument(s) to the end of the list being made, and `yoke`
# adds its argument(s) to the front. `link` and `yoke` only make sense if there
# is an active `make` call. After evaluating its arguments, `make` returns the
# list just made.
(de parse () # parse a single instruction, `link`ing the instruction if valid
(case (char) # consume a single character
(">" (link '(bf-right)))
("<" (link '(bf-left)))
("+" (link (snarf-adds 1)))
("-" (link (snarf-adds -1)))
("." (link '(bf-out)))
("," (link '(bf-in)))
("[" (link (cons 'bf-loop (make
(until (or (eof)
(= (peek) "]"))
(parse))
(char)))))))
(de snarf-adds (n) # consume consecutive "-" and "+" and produce one `bf-add` instruction
(while (not (or (eof)
(member (peek)
(list ">" "<" "." "," "[" "]"))))
(case (char)
("+" (inc 'n))
("-" (dec 'n))))
(list 'bf-add n))
(de parse-prog () # read until `(eof)` producing a `(prog)` that ends with `(bye)`
(cons 'prog
(make (until (eof) (parse))
(link '(bye)))))
##########
# Compiler
##########
(de x () # parse the given file, then run the result
(let p (in (opt) (parse-prog))
(p)))
(de o () # set `outfile` to the next argument
(setq outfile (opt)))
(de c () # parse the given file, then write the result to the given file or stdout
(let p (in (opt) (parse-prog))
(if outfile
(out outfile (print p))
(print p)))
(bye))