about summary refs log tree commit diff stats
path: root/Makefile
blob: b139b544f91a86994ca125a229540e13ea7ab25a (plain) (blame)
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
# dwm - dynamic window manager
# See LICENSE file for copyright and license details.

include config.mk

SRC = dwm.c
OBJ = ${SRC:.c=.o}

all: options dwm

options:
	@echo dwm build options:
	@echo "CFLAGS   = ${CFLAGS}"
	@echo "LDFLAGS  = ${LDFLAGS}"
	@echo "CC       = ${CC}"

.c.o:
	@echo CC $<
	@${CC} -c ${CFLAGS} $<

${OBJ}: config.h config.mk

config.h:
	@echo creating $@ from config.def.h
	@cp config.def.h $@

dwm: ${OBJ}
	@echo CC -o $@
	@${CC} -o $@ ${OBJ} ${LDFLAGS}

clean:
	@echo cleaning
	@rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz

dist: clean
	@echo creating dist tarball
	@mkdir -p dwm-${VERSION}
	@cp -R LICENSE Makefile README config.def.h config.mk \
		dwm.1 ${SRC} dwm-${VERSION}
	@tar -cf dwm-${VERSION}.tar dwm-${VERSION}
	@gzip dwm-${VERSION}.tar
	@rm -rf dwm-${VERSION}

install: all
	@echo installing executable file to ${DESTDIR}${PREFIX}/bin
	@mkdir -p ${DESTDIR}${PREFIX}/bin
	@cp -f dwm ${DESTDIR}${PREFIX}/bin
	@chmod 755 ${DESTDIR}${PREFIX}/bin/dwm
	@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
	@mkdir -p ${DESTDIR}${MANPREFIX}/man1
	@sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
	@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1

uninstall:
	@echo removing executable file from ${DESTDIR}${PREFIX}/bin
	@rm -f ${DESTDIR}${PREFIX}/bin/dwm
	@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
	@rm -f ${DESTDIR}${MANPREFIX}/man1/dwm.1

.PHONY: all options clean dist install uninstall
an class="w"> monospace; color: #aaaaaa; background-color: #080808; } body { font-size: 12pt; font-family: monospace; color: #aaaaaa; background-color: #080808; } a { color:#eeeeee; text-decoration: none; } a:hover { text-decoration: underline; } * { font-size: 12pt; font-size: 1em; } .muControl { color: #c0a020; } .muRecipe { color: #ff8700; } .Special { color: #c00000; } .Delimiter { color: #800080; } .muData { color: #ffff00; } .Comment { color: #9090ff; } .Comment a { color:#0000ee; text-decoration:underline; } .Constant { color: #00a0a0; } .LineNr { color: #444444; } .muScenario { color: #00af00; } --> </style> <script type='text/javascript'> <!-- /* function to open any folds containing a jumped-to line before jumping to it */ function JumpToLine() { var lineNum; lineNum = window.location.hash; lineNum = lineNum.substr(1); /* strip off '#' */ if (lineNum.indexOf('L') == -1) { lineNum = 'L'+lineNum; } lineElem = document.getElementById(lineNum); /* Always jump to new location even if the line was hidden inside a fold, or * we corrected the raw number to a line ID. */ if (lineElem) { lineElem.scrollIntoView(true); } return true; } if ('onhashchange' in window) { window.onhashchange = JumpToLine; } --> </script> </head> <body onload='JumpToLine();'> <pre id='vimCodeElement'> <span id="L1" class="LineNr"> 1 </span><span class="Comment"># Wrappers around socket primitives that are easier to test.</span> <span id="L2" class="LineNr"> 2 </span> <span id="L3" class="LineNr"> 3 </span><span class="Comment"># To test server operations, just run a real client against localhost.</span> <span id="L4" class="LineNr"> 4 </span><span class="muScenario">scenario</span> example-server-test [ <span id="L5" class="LineNr"> 5 </span> <span class="Constant">local-scope</span> <span id="L6" class="LineNr"> 6 </span> <span class="Comment"># test server without a fake on a random (real) port</span> <span id="L7" class="LineNr"> 7 </span> <span class="Comment"># that way repeatedly running the test will give ports time to timeout and</span> <span id="L8" class="LineNr"> 8 </span> <span class="Comment"># close before reusing them</span> <span id="L9" class="LineNr"> 9 </span> make-random-nondeterministic <span id="L10" class="LineNr"> 10 </span> port:num <span class="Special">&lt;-</span> <a href='068random.mu.html#L58'>random-in-range</a><span class="Constant"> null/real-random-numbers, 8000</span>,<span class="Constant"> 8100</span> <span id="L11" class="LineNr"> 11 </span> run [ <span id="L12" class="LineNr"> 12 </span> socket:num <span class="Special">&lt;-</span> $open-server-socket port <span id="L13" class="LineNr"> 13 </span> assert socket, <span class="Constant">[ </span> <span id="L14" class="LineNr"> 14 </span><span class="Constant">F - example-server-test: $open-server-socket failed]</span> <span id="L15" class="LineNr"> 15 </span> handler-routine:number <span class="Special">&lt;-</span> <span class="muControl">start-running</span> <a href='092socket.mu.html#L55'>serve-one-request</a> socket, <a href='092socket.mu.html#L26'>example-handler</a> <span id="L16" class="LineNr"> 16 </span> ] <span id="L17" class="LineNr"> 17 </span> <a href='075channel.mu.html#L36'>source</a>:&amp;:<a href='075channel.mu.html#L36'>source</a>:char <span class="Special">&lt;-</span> <a href='092socket.mu.html#L69'>start-reading-from-network</a><span class="Constant"> null/real-resources,</span> <span class="Constant">[localhost/]</span>, port <span id="L18" class="LineNr"> 18 </span> response:text <span class="Special">&lt;-</span> <a href='075channel.mu.html#L499'>drain</a> <a href='075channel.mu.html#L36'>source</a> <span id="L19" class="LineNr"> 19 </span> 10:@:char/<span class="Special">raw</span> <span class="Special">&lt;-</span> copy *response <span id="L20" class="LineNr"> 20 </span> memory-should-contain [ <span id="L21" class="LineNr"> 21 </span> 10:array:character <span class="Special">&lt;-</span> <span class="Constant">[abc]</span> <span id="L22" class="LineNr"> 22 </span> ] <span id="L23" class="LineNr"> 23 </span> socket <span class="Special">&lt;-</span> $close-socket socket <span id="L24" class="LineNr"> 24 </span>] <span id="L25" class="LineNr"> 25 </span><span class="Comment"># helper just for this scenario</span> <span id="L26" class="LineNr"> 26 </span><span class="muRecipe">def</span> <a href='092socket.mu.html#L26'>example-handler</a> query:text<span class="muRecipe"> -&gt; </span>response:text [ <span id="L27" class="LineNr"> 27 </span> <span class="Constant">local-scope</span> <span id="L28" class="LineNr"> 28 </span> <span class="Constant">load-inputs</span> <span id="L29" class="LineNr"> 29 </span> <span class="muControl"> return</span> <span class="Constant">[abc]</span> <span id="L30" class="LineNr"> 30 </span>] <span id="L31" class="LineNr"> 31 </span> <span id="L32" class="LineNr"> 32 </span><span class="Comment"># To test client operations, use 'assume-resources' with a filename that</span> <span id="L33" class="LineNr"> 33 </span><span class="Comment"># begins with a hostname. (Filenames starting with '/' are assumed to be</span> <span id="L34" class="LineNr"> 34 </span><span class="Comment"># local.)</span> <span id="L35" class="LineNr"> 35 </span><span class="muScenario">scenario</span> example-client-test [ <span id="L36" class="LineNr"> 36 </span> <span class="Constant">local-scope</span> <span id="L37" class="LineNr"> 37 </span> assume-resources [ <span id="L38" class="LineNr"> 38 </span> <span class="Constant">[example.com/]</span> <span class="Special">&lt;-</span> [ <span id="L39" class="LineNr"> 39 </span><span class="Constant"> |abc|</span> <span id="L40" class="LineNr"> 40 </span> ] <span id="L41" class="LineNr"> 41 </span> ] <span id="L42" class="LineNr"> 42 </span> run [ <span id="L43" class="LineNr"> 43 </span> <a href='075channel.mu.html#L36'>source</a>:&amp;:<a href='075channel.mu.html#L36'>source</a>:char <span class="Special">&lt;-</span> <a href='092socket.mu.html#L69'>start-reading-from-network</a> <a href='088file.mu.html#L11'>resources</a>, <span class="Constant">[example.com/]</span> <span id="L44" class="LineNr"> 44 </span> ] <span id="L45" class="LineNr"> 45 </span> contents:text <span class="Special">&lt;-</span> <a href='075channel.mu.html#L499'>drain</a> <a href='075channel.mu.html#L36'>source</a> <span id="L46" class="LineNr"> 46 </span> 10:@:char/<span class="Special">raw</span> <span class="Special">&lt;-</span> copy *contents <span id="L47" class="LineNr"> 47 </span> memory-should-contain [ <span id="L48" class="LineNr"> 48 </span> 10:array:character <span class="Special">&lt;-</span> <span class="Constant">[abc</span> <span id="L49" class="LineNr"> 49 </span><span class="Constant">]</span> <span id="L50" class="LineNr"> 50 </span> ] <span id="L51" class="LineNr"> 51 </span>] <span id="L52" class="LineNr"> 52 </span> <span id="L53" class="LineNr"> 53 </span><span class="muData">type</span> <a href='092socket.mu.html#L53'>request-handler</a> = (recipe text<span class="muRecipe"> -&gt; </span>text) <span id="L54" class="LineNr"> 54 </span> <span id="L55" class="LineNr"> 55 </span><span class="muRecipe">def</span> <a href='092socket.mu.html#L55'>serve-one-request</a> socket:num, <a href='092socket.mu.html#L53'>request-handler</a>:<a href='092socket.mu.html#L53'>request-handler</a><span class="muRecipe"> -&gt; </span>socket:num [ <span id="L56" class="LineNr"> 56 </span> <span class="Constant">local-scope</span> <span id="L57" class="LineNr"> 57 </span> <span class="Constant">load-inputs</span> <span id="L58" class="LineNr"> 58 </span> session:num <span class="Special">&lt;-</span> $accept socket <span id="L59" class="LineNr"> 59 </span> assert session, <span class="Constant">[ </span> <span id="L60" class="LineNr"> 60 </span><span class="Constant">F - example-server-test: $accept failed]</span> <span id="L61" class="LineNr"> 61 </span> contents:&amp;:<a href='075channel.mu.html#L36'>source</a>:char, <a href='075channel.mu.html#L40'>sink</a>:&amp;:<a href='075channel.mu.html#L40'>sink</a>:char <span class="Special">&lt;-</span> <a href='075channel.mu.html#L44'>new-channel</a><span class="Constant"> 30</span> <span id="L62" class="LineNr"> 62 </span> <span class="muControl">start-running</span> <a href='092socket.mu.html#L104'>receive-from-socket</a> session, <a href='075channel.mu.html#L40'>sink</a> <span id="L63" class="LineNr"> 63 </span> query:text <span class="Special">&lt;-</span> <a href='075channel.mu.html#L499'>drain</a> contents <span id="L64" class="LineNr"> 64 </span> response:text <span class="Special">&lt;-</span> call <a href='092socket.mu.html#L53'>request-handler</a>, query <span id="L65" class="LineNr"> 65 </span> <a href='092socket.mu.html#L132'>write-to-socket</a> session, response <span id="L66" class="LineNr"> 66 </span> session <span class="Special">&lt;-</span> $close-socket session <span id="L67" class="LineNr"> 67 </span>] <span id="L68" class="LineNr"> 68 </span> <span id="L69" class="LineNr"> 69 </span><span class="muRecipe">def</span> <a href='092socket.mu.html#L69'>start-reading-from-network</a> <a href='088file.mu.html#L11'>resources</a>:&amp;:<a href='088file.mu.html#L11'>resources</a>, uri:text<span class="muRecipe"> -&gt; </span>contents:&amp;:<a href='075channel.mu.html#L36'>source</a>:char [ <span id="L70" class="LineNr"> 70 </span> <span class="Constant">local-scope</span> <span id="L71" class="LineNr"> 71 </span> <span class="Constant">load-inputs</span> <span id="L72" class="LineNr"> 72 </span> <span class="Delimiter">{</span> <span id="L73" class="LineNr"> 73 </span> port:num, port-found?:boolean <span class="Special">&lt;-</span> <span class="Constant">next-input</span> <span id="L74" class="LineNr"> 74 </span> <span class="muControl">break-if</span> port-found? <span id="L75" class="LineNr"> 75 </span> port <span class="Special">&lt;-</span> copy <span class="Constant">80/http-port</span> <span id="L76" class="LineNr"> 76 </span> <span class="Delimiter">}</span> <span id="L77" class="LineNr"> 77 </span> <span class="Delimiter">{</span> <span id="L78" class="LineNr"> 78 </span> <span class="muControl">break-unless</span> <a href='088file.mu.html#L11'>resources</a> <span id="L79" class="LineNr"> 79 </span> <span class="Comment"># fake network</span> <span id="L80" class="LineNr"> 80 </span> contents <span class="Special">&lt;-</span> <a href='088file.mu.html#L53'>start-reading-from-fake-resource</a> <a href='088file.mu.html#L11'>resources</a>, uri <span id="L81" class="LineNr"> 81 </span> <span class="muControl"> return</span> <span id="L82" class="LineNr"> 82 </span> <span class="Delimiter">}</span> <span id="L83" class="LineNr"> 83 </span> <span class="Comment"># real network</span> <span id="L84" class="LineNr"> 84 </span> host:text, path:text <span class="Special">&lt;-</span> <a href='092socket.mu.html#L148'>split-at</a> uri, <span class="Constant">47/slash</span> <span id="L85" class="LineNr"> 85 </span> socket:num <span class="Special">&lt;-</span> $open-client-socket host, port <span id="L86" class="LineNr"> 86 </span> assert socket, <span class="Constant">[contents]</span> <span id="L87" class="LineNr"> 87 </span> req:text <span class="Special">&lt;-</span> <a href='061text.mu.html#L520'>interpolate</a> <span class="Constant">[GET _ HTTP/1.1]</span>, path <span id="L88" class="LineNr"> 88 </span> <a href='092socket.mu.html#L93'>request-socket</a> socket, req <span id="L89" class="LineNr"> 89 </span> contents:&amp;:<a href='075channel.mu.html#L36'>source</a>:char, <a href='075channel.mu.html#L40'>sink</a>:&amp;:<a href='075channel.mu.html#L40'>sink</a>:char <span class="Special">&lt;-</span> <a href='075channel.mu.html#L44'>new-channel</a><span class="Constant"> 10000</span> <span id="L90" class="LineNr"> 90 </span> <span class="muControl">start-running</span> <a href='092socket.mu.html#L125'>receive-from-client-socket-and-close</a> socket, <a href='075channel.mu.html#L40'>sink</a> <span id="L91" class="LineNr"> 91 </span>] <span id="L92" class="LineNr"> 92 </span> <span id="L93" class="LineNr"> 93 </span><span class="muRecipe">def</span> <a href='092socket.mu.html#L93'>request-socket</a> socket:num, s:text<span class="muRecipe"> -&gt; </span>socket:num [ <span id="L94" class="LineNr"> 94 </span> <span class="Constant">local-scope</span> <span id="L95" class="LineNr"> 95 </span> <span class="Constant">load-inputs</span> <span id="L96" class="LineNr"> 96 </span> <a href='092socket.mu.html#L132'>write-to-socket</a> socket, s <span id="L97" class="LineNr"> 97 </span> $write-to-socket socket, <span class="Constant">13/cr</span> <span id="L98" class="LineNr"> 98 </span> $write-to-socket socket, <span class="Constant">10/lf</span> <span id="L99" class="LineNr"> 99 </span> <span class="Comment"># empty line to delimit request</span> <span id="L100" class="LineNr">100 </span> $write-to-socket socket, <span class="Constant">13/cr</span> <span id="L101" class="LineNr">101 </span> $write-to-socket socket, <span class="Constant">10/lf</span> <span id="L102" class="LineNr">102 </span>] <span id="L103" class="LineNr">103 </span> <span id="L104" class="LineNr">104 </span><span class="muRecipe">def</span> <a href='092socket.mu.html#L104'>receive-from-socket</a> socket:num, <a href='075channel.mu.html#L40'>sink</a>:&amp;:<a href='075channel.mu.html#L40'>sink</a>:char<span class="muRecipe"> -&gt; </span><a href='075channel.mu.html#L40'>sink</a>:&amp;:<a href='075channel.mu.html#L40'>sink</a>:char, socket:num [ <span id="L105" class="LineNr">105 </span> <span class="Constant">local-scope</span> <span id="L106" class="LineNr">106 </span> <span class="Constant">load-inputs</span> <span id="L107" class="LineNr">107 </span> <span class="Delimiter">{</span> <span id="L108" class="LineNr">108 </span><span class="Constant"> +next-attempt</span> <span id="L109" class="LineNr">109 </span> c:char, found?:bool, eof?:bool, error:num <span class="Special">&lt;-</span> $read-from-socket socket <span id="L110" class="LineNr">110 </span> <span class="muControl">break-if</span> eof? <span id="L111" class="LineNr">111 </span> <span class="muControl">break-if</span> error <span id="L112" class="LineNr">112 </span> <span class="Delimiter">{</span> <span id="L113" class="LineNr">113 </span> <span class="muControl">break-unless</span> found? <span id="L114" class="LineNr">114 </span> <a href='075channel.mu.html#L40'>sink</a> <span class="Special">&lt;-</span> <a href='075channel.mu.html#L60'>write</a> <a href='075channel.mu.html#L40'>sink</a>, c <span id="L115" class="LineNr">115 </span> <span class="Delimiter">}</span> <span id="L116" class="LineNr">116 </span> <span class="Delimiter">{</span> <span id="L117" class="LineNr">117 </span> <span class="muControl">break-if</span> found? <span id="L118" class="LineNr">118 </span> switch <span id="L119" class="LineNr">119 </span> <span class="Delimiter">}</span> <span id="L120" class="LineNr">120 </span> <span class="muControl"> loop</span> <span id="L121" class="LineNr">121 </span> <span class="Delimiter">}</span> <span id="L122" class="LineNr">122 </span> <a href='075channel.mu.html#L40'>sink</a> <span class="Special">&lt;-</span> close <a href='075channel.mu.html#L40'>sink</a> <span id="L123" class="LineNr">123 </span>] <span id="L124" class="LineNr">124 </span> <span id="L125" class="LineNr">125 </span><span class="muRecipe">def</span> <a href='092socket.mu.html#L125'>receive-from-client-socket-and-close</a> socket:num, <a href='075channel.mu.html#L40'>sink</a>:&amp;:<a href='075channel.mu.html#L40'>sink</a>:char<span class="muRecipe"> -&gt; </span><a href='075channel.mu.html#L40'>sink</a>:&amp;:<a href='075channel.mu.html#L40'>sink</a>:char, socket:num [ <span id="L126" class="LineNr">126 </span> <span class="Constant">local-scope</span> <span id="L127" class="LineNr">127 </span> <span class="Constant">load-inputs</span> <span id="L128" class="LineNr">128 </span> <a href='075channel.mu.html#L40'>sink</a> <span class="Special">&lt;-</span> <a href='092socket.mu.html#L104'>receive-from-socket</a> socket, <a href='075channel.mu.html#L40'>sink</a> <span id="L129" class="LineNr">129 </span> socket <span class="Special">&lt;-</span> $close-socket socket <span id="L130" class="LineNr">130 </span>] <span id="L131" class="LineNr">131 </span> <span id="L132" class="LineNr">132 </span><span class="muRecipe">def</span> <a href='092socket.mu.html#L132'>write-to-socket</a> socket:num, s:text [ <span id="L133" class="LineNr">133 </span> <span class="Constant">local-scope</span> <span id="L134" class="LineNr">134 </span> <span class="Constant">load-inputs</span> <span id="L135" class="LineNr">135 </span> len:num <span class="Special">&lt;-</span> length *s <span id="L136" class="LineNr">136 </span> i:num <span class="Special">&lt;-</span> copy<span class="Constant"> 0</span> <span id="L137" class="LineNr">137 </span> <span class="Delimiter">{</span> <span id="L138" class="LineNr">138 </span> done?:bool <span class="Special">&lt;-</span> greater-or-equal i, len <span id="L139" class="LineNr">139 </span> <span class="muControl">break-if</span> done? <span id="L140" class="LineNr">140 </span> c:char <span class="Special">&lt;-</span> index *s, i <span id="L141" class="LineNr">141 </span> $write-to-socket socket, c <span id="L142" class="LineNr">142 </span> i <span class="Special">&lt;-</span> add i,<span class="Constant"> 1</span> <span id="L143" class="LineNr">143 </span> <span class="muControl"> loop</span> <span id="L144" class="LineNr">144 </span> <span class="Delimiter">}</span> <span id="L145" class="LineNr">145 </span>] <span id="L146" class="LineNr">146 </span> <span id="L147" class="LineNr">147 </span><span class="Comment"># like split-first, but don't eat the delimiter</span> <span id="L148" class="LineNr">148 </span><span class="muRecipe">def</span> <a href='092socket.mu.html#L148'>split-at</a> text:text, delim:char<span class="muRecipe"> -&gt; </span>x:text, y:text [ <span id="L149" class="LineNr">149 </span> <span class="Constant">local-scope</span> <span id="L150" class="LineNr">150 </span> <span class="Constant">load-inputs</span> <span id="L151" class="LineNr">151 </span> <span class="Comment"># empty text? return empty texts</span> <span id="L152" class="LineNr">152 </span> len:num <span class="Special">&lt;-</span> length *text <span id="L153" class="LineNr">153 </span> <span class="Delimiter">{</span> <span id="L154" class="LineNr">154 </span> empty?:bool <span class="Special">&lt;-</span> equal len,<span class="Constant"> 0</span> <span id="L155" class="LineNr">155 </span> <span class="muControl">break-unless</span> empty? <span id="L156" class="LineNr">156 </span> x:text <span class="Special">&lt;-</span> new <span class="Constant">[]</span> <span id="L157" class="LineNr">157 </span> y:text <span class="Special">&lt;-</span> new <span class="Constant">[]</span> <span id="L158" class="LineNr">158 </span> <span class="muControl"> return</span> <span id="L159" class="LineNr">159 </span> <span class="Delimiter">}</span> <span id="L160" class="LineNr">160 </span> idx:num <span class="Special">&lt;-</span> find-next text, delim,<span class="Constant"> 0</span> <span id="L161" class="LineNr">161 </span> x:text <span class="Special">&lt;-</span> <a href='061text.mu.html#L1287'>copy-range</a> text,<span class="Constant"> 0</span>, idx <span id="L162" class="LineNr">162 </span> y:text <span class="Special">&lt;-</span> <a href='061text.mu.html#L1287'>copy-range</a> text, idx, len <span id="L163" class="LineNr">163 </span>] <span id="L164" class="LineNr">164 </span> <span id="L165" class="LineNr">165 </span><span class="muScenario">scenario</span> text-split-at [ <span id="L166" class="LineNr">166 </span> <span class="Constant">local-scope</span> <span id="L167" class="LineNr">167 </span> x:text <span class="Special">&lt;-</span> new <span class="Constant">[a/b]</span> <span id="L168" class="LineNr">168 </span> run [ <span id="L169" class="LineNr">169 </span> y:text, z:text <span class="Special">&lt;-</span> <a href='092socket.mu.html#L148'>split-at</a> x, <span class="Constant">47/slash</span> <span id="L170" class="LineNr">170 </span> 10:@:char/<span class="Special">raw</span> <span class="Special">&lt;-</span> copy *y <span id="L171" class="LineNr">171 </span> 20:@:char/<span class="Special">raw</span> <span class="Special">&lt;-</span> copy *z <span id="L172" class="LineNr">172 </span> ] <span id="L173" class="LineNr">173 </span> memory-should-contain [ <span id="L174" class="LineNr">174 </span> 10:array:character <span class="Special">&lt;-</span> <span class="Constant">[a]</span> <span id="L175" class="LineNr">175 </span> 20:array:character <span class="Special">&lt;-</span> <span class="Constant">[/b]</span> <span id="L176" class="LineNr">176 </span> ] <span id="L177" class="LineNr">177 </span>] </pre> </body> </html> <!-- vim: set foldmethod=manual : --> :@:char/raw <- copy *x ] memory-should-contain [ 10:array:character <- [azc] ] ] def replace s:text, oldc:char, newc:char, from:num/optional -> s:text [ local-scope load-inputs len:num <- length *s i:num <- find-next s, oldc, from done?:bool <- greater-or-equal i, len return-if done? *s <- put-index *s, i, newc i <- add i, 1 s <- replace s, oldc, newc, i ] scenario replace-character-at-start [ local-scope x:text <- new [abc] run [ x <- replace x, 97/a, 122/z 10:@:char/raw <- copy *x ] memory-should-contain [ 10:array:character <- [zbc] ] ] scenario replace-character-at-end [ local-scope x:text <- new [abc] run [ x <- replace x, 99/c, 122/z 10:@:char/raw <- copy *x ] memory-should-contain [ 10:array:character <- [abz] ] ] scenario replace-character-missing [ local-scope x:text <- new [abc] run [ x <- replace x, 100/d, 122/z 10:@:char/raw <- copy *x ] memory-should-contain [ 10:array:character <- [abc] ] ] scenario replace-all-characters [ local-scope x:text <- new [banana] run [ x <- replace x, 97/a, 122/z 10:@:char/raw <- copy *x ] memory-should-contain [ 10:array:character <- [bznznz] ] ] # replace underscores in first with remaining args def interpolate template:text -> result:text [ local-scope load-inputs # consume just the template # compute result-len, space to allocate for result tem-len:num <- length *template result-len:num <- copy tem-len { # while inputs remain a:text, arg-received?:bool <- next-input break-unless arg-received? # result-len = result-len + arg.length - 1 (for the 'underscore' being replaced) a-len:num <- length *a result-len <- add result-len, a-len result-len <- subtract result-len, 1 loop } rewind-inputs _ <- next-input # skip template result <- new character:type, result-len # repeatedly copy sections of template and 'holes' into result result-idx:num <- copy 0 i:num <- copy 0 { # while arg received a:text, arg-received?:bool <- next-input break-unless arg-received? # copy template into result until '_' { # while i < template.length tem-done?:bool <- greater-or-equal i, tem-len break-if tem-done?, +done # while template[i] != '_' in:char <- index *template, i underscore?:bool <- equal in, 95/_ break-if underscore? # result[result-idx] = template[i] *result <- put-index *result, result-idx, in i <- add i, 1 result-idx <- add result-idx, 1 loop } # copy 'a' into result j:num <- copy 0 { # while j < a.length arg-done?:bool <- greater-or-equal j, a-len break-if arg-done? # result[result-idx] = a[j] in:char <- index *a, j *result <- put-index *result, result-idx, in j <- add j, 1 result-idx <- add result-idx, 1 loop } # skip '_' in template i <- add i, 1 loop # interpolate next arg } +done # done with holes; copy rest of template directly into result { # while i < template.length tem-done?:bool <- greater-or-equal i, tem-len break-if tem-done? # result[result-idx] = template[i] in:char <- index *template, i *result <- put-index *result, result-idx, in i <- add i, 1 result-idx <- add result-idx, 1 loop } ] scenario interpolate-works [ local-scope x:text <- new [abc_ghi] y:text <- new [def] run [ z:text <- interpolate x, y 10:@:char/raw <- copy *z ] memory-should-contain [ 10:array:character <- [abcdefghi] ] ] scenario interpolate-at-start [ local-scope x:text <- new [_, hello!] y:text <- new [abc] run [ z:text <- interpolate x, y 10:@:char/raw <- copy *z ] memory-should-contain [ 10:array:character <- [abc, hello!] 22 <- 0 # out of bounds ] ] scenario interpolate-at-end [ local-scope x:text <- new [hello, _] y:text <- new [abc] run [ z:text <- interpolate x, y 10:@:char/raw <- copy *z ] memory-should-contain [ 10:array:character <- [hello, abc] ] ] # result:bool <- space? c:char def space? c:char -> result:bool [ local-scope load-inputs # most common case first result <- equal c, 32/space return-if result result <- equal c, 10/newline return-if result result <- equal c, 9/tab return-if result result <- equal c, 13/carriage-return return-if result # remaining uncommon cases in sorted order # http://unicode.org code-points in unicode-set Z and Pattern_White_Space result <- equal c, 11/ctrl-k return-if result result <- equal c, 12/ctrl-l return-if result result <- equal c, 133/ctrl-0085 return-if result result <- equal c, 160/no-break-space return-if result result <- equal c, 5760/ogham-space-mark return-if result result <- equal c, 8192/en-quad return-if result result <- equal c, 8193/em-quad return-if result result <- equal c, 8194/en-space return-if result result <- equal c, 8195/em-space return-if result result <- equal c, 8196/three-per-em-space return-if result result <- equal c, 8197/four-per-em-space return-if result result <- equal c, 8198/six-per-em-space return-if result result <- equal c, 8199/figure-space return-if result result <- equal c, 8200/punctuation-space return-if result result <- equal c, 8201/thin-space return-if result result <- equal c, 8202/hair-space return-if result result <- equal c, 8206/left-to-right return-if result result <- equal c, 8207/right-to-left return-if result result <- equal c, 8232/line-separator return-if result result <- equal c, 8233/paragraph-separator return-if result result <- equal c, 8239/narrow-no-break-space return-if result result <- equal c, 8287/medium-mathematical-space return-if result result <- equal c, 12288/ideographic-space ] def trim s:text -> result:text [ local-scope load-inputs len:num <- length *s # left trim: compute start start:num <- copy 0 { { at-end?:bool <- greater-or-equal start, len break-unless at-end? result <- new character:type, 0 return } curr:char <- index *s, start whitespace?:bool <- space? curr break-unless whitespace? start <- add start, 1 loop } # right trim: compute end end:num <- subtract len, 1 { not-at-start?:bool <- greater-than end, start assert not-at-start?, [end ran up against start] curr:char <- index *s, end whitespace?:bool <- space? curr break-unless whitespace? end <- subtract end, 1 loop } # result = new character[end+1 - start] new-len:num <- subtract end, start, -1 result:text <- new character:type, new-len # copy the untrimmed parts between start and end i:num <- copy start j:num <- copy 0 { # while i <= end done?:bool <- greater-than i, end break-if done? # result[j] = s[i] src:char <- index *s, i *result <- put-index *result, j, src i <- add i, 1 j <- add j, 1 loop } ] scenario trim-unmodified [ local-scope x:text <- new [abc] run [ y:text <- trim x 1:@:char/raw <- copy *y ] memory-should-contain [ 1:array:character <- [abc] ] ] scenario trim-left [ local-scope x:text <- new [ abc] run [ y:text <- trim x 1:@:char/raw <- copy *y ] memory-should-contain [ 1:array:character <- [abc] ] ] scenario trim-right [ local-scope x:text <- new [abc ] run [ y:text <- trim x 1:@:char/raw <- copy *y ] memory-should-contain [ 1:array:character <- [abc] ] ] scenario trim-left-right [ local-scope x:text <- new [ abc ] run [ y:text <- trim x 1:@:char/raw <- copy *y ] memory-should-contain [ 1:array:character <- [abc] ] ] scenario trim-newline-tab [ local-scope x:text <- new [ abc ] run [ y:text <- trim x 1:@:char/raw <- copy *y ] memory-should-contain [ 1:array:character <- [abc] ] ] def find-next text:text, pattern:char, idx:num -> next-index:num [ local-scope load-inputs len:num <- length *text { eof?:bool <- greater-or-equal idx, len break-if eof? curr:char <- index *text, idx found?:bool <- equal curr, pattern break-if found? idx <- add idx, 1 loop } return idx ] scenario text-find-next [ local-scope x:text <- new [a/b] run [ 10:num/raw <- find-next x, 47/slash, 0/start-index ] memory-should-contain [ 10 <- 1 ] ] scenario text-find-next-empty [ local-scope x:text <- new [] run [ 10:num/raw <- find-next x, 47/slash, 0/start-index ] memory-should-contain [ 10 <- 0 ] ] scenario text-find-next-initial [ local-scope x:text <- new [/abc] run [ 10:num/raw <- find-next x, 47/slash, 0/start-index ] memory-should-contain [ 10 <- 0 # prefix match ] ] scenario text-find-next-final [ local-scope x:text <- new [abc/] run [ 10:num/raw <- find-next x, 47/slash, 0/start-index ] memory-should-contain [ 10 <- 3 # suffix match ] ] scenario text-find-next-missing [ local-scope x:text <- new [abcd] run [ 10:num/raw <- find-next x, 47/slash, 0/start-index ] memory-should-contain [ 10 <- 4 # no match ] ] scenario text-find-next-invalid-index [ local-scope x:text <- new [abc] run [ 10:num/raw <- find-next x, 47/slash, 4/start-index ] memory-should-contain [ 10 <- 4 # no change ] ] scenario text-find-next-first [ local-scope x:text <- new [ab/c/] run [ 10:num/raw <- find-next x, 47/slash, 0/start-index ] memory-should-contain [ 10 <- 2 # first '/' of multiple ] ] scenario text-find-next-second [ local-scope x:text <- new [ab/c/] run [ 10:num/raw <- find-next x, 47/slash, 3/start-index ] memory-should-contain [ 10 <- 4 # second '/' of multiple ] ] # search for a pattern of multiple characters # fairly dumb algorithm def find-next text:text, pattern:text, idx:num -> next-index:num [ local-scope load-inputs first:char <- index *pattern, 0 # repeatedly check for match at current idx len:num <- length *text { # does some unnecessary work checking even when there isn't enough of text left done?:bool <- greater-or-equal idx, len break-if done? found?:bool <- match-at text, pattern, idx break-if found? idx <- add idx, 1 # optimization: skip past indices that definitely won't match idx <- find-next text, first, idx loop } return idx ] scenario find-next-text-1 [ local-scope x:text <- new [abc] y:text <- new [bc] run [ 10:num/raw <- find-next x, y, 0 ] memory-should-contain [ 10 <- 1 ] ] scenario find-next-text-2 [ local-scope x:text <- new [abcd] y:text <- new [bc] run [ 10:num/raw <- find-next x, y, 1 ] memory-should-contain [ 10 <- 1 ] ] scenario find-next-no-match [ local-scope x:text <- new [abc] y:text <- new [bd] run [ 10:num/raw <- find-next x, y, 0 ] memory-should-contain [ 10 <- 3 # not found ] ] scenario find-next-suffix-match [ local-scope x:text <- new [abcd] y:text <- new [cd] run [ 10:num/raw <- find-next x, y, 0 ] memory-should-contain [ 10 <- 2 ] ] scenario find-next-suffix-match-2 [ local-scope x:text <- new [abcd] y:text <- new [cde] run [ 10:num/raw <- find-next x, y, 0 ] memory-should-contain [ 10 <- 4 # not found ] ] # checks if pattern matches at index 'idx' def match-at text:text, pattern:text, idx:num -> result:bool [ local-scope load-inputs pattern-len:num <- length *pattern # check that there's space left for the pattern x:num <- length *text x <- subtract x, pattern-len enough-room?:bool <- lesser-or-equal idx, x return-unless enough-room?, false/not-found # check each character of pattern pattern-idx:num <- copy 0 { done?:bool <- greater-or-equal pattern-idx, pattern-len break-if done? c:char <- index *text, idx exp:char <- index *pattern, pattern-idx match?:bool <- equal c, exp return-unless match?, false/not-found idx <- add idx, 1 pattern-idx <- add pattern-idx, 1 loop } return true/found ] scenario match-at-checks-pattern-at-index [ local-scope x:text <- new [abc] y:text <- new [ab] run [ 10:bool/raw <- match-at x, y, 0 ] memory-should-contain [ 10 <- 1 # match found ] ] scenario match-at-reflexive [ local-scope x:text <- new [abc] run [ 10:bool/raw <- match-at x, x, 0 ] memory-should-contain [ 10 <- 1 # match found ] ] scenario match-at-outside-bounds [ local-scope x:text <- new [abc] y:text <- new [a] run [ 10:bool/raw <- match-at x, y, 4 ] memory-should-contain [ 10 <- 0 # never matches ] ] scenario match-at-empty-pattern [ local-scope x:text <- new [abc] y:text <- new [] run [ 10:bool/raw <- match-at x, y, 0 ] memory-should-contain [ 10 <- 1 # always matches empty pattern given a valid index ] ] scenario match-at-empty-pattern-outside-bound [ local-scope x:text <- new [abc] y:text <- new [] run [ 10:bool/raw <- match-at x, y, 4 ] memory-should-contain [ 10 <- 0 # no match ] ] scenario match-at-empty-text [ local-scope x:text <- new [] y:text <- new [abc] run [ 10:bool/raw <- match-at x, y, 0 ] memory-should-contain [ 10 <- 0 # no match ] ] scenario match-at-empty-against-empty [ local-scope x:text <- new [] run [ 10:bool/raw <- match-at x, x, 0 ] memory-should-contain [ 10 <- 1 # matches because pattern is also empty ] ] scenario match-at-inside-bounds [ local-scope x:text <- new [abc] y:text <- new [bc] run [ 10:bool/raw <- match-at x, y, 1 ] memory-should-contain [ 10 <- 1 # match ] ] scenario match-at-inside-bounds-2 [ local-scope x:text <- new [abc] y:text <- new [bc] run [ 10:bool/raw <- match-at x, y, 0 ] memory-should-contain [ 10 <- 0 # no match ] ] def split s:text, delim:char -> result:&:@:text [ local-scope load-inputs # empty text? return empty array len:num <- length *s { empty?:bool <- equal len, 0 break-unless empty? result <- new {(address array character): type}, 0 return } # count #pieces we need room for count:num <- copy 1 # n delimiters = n+1 pieces idx:num <- copy 0 { idx <- find-next s, delim, idx done?:bool <- greater-or-equal idx, len break-if done? idx <- add idx, 1 count <- add count, 1 loop } # allocate space result <- new {(address array character): type}, count # repeatedly copy slices start..end until delimiter into result[curr-result] curr-result:num <- copy 0 start:num <- copy 0 { # while next delim exists done?:bool <- greater-or-equal start, len break-if done? end:num <- find-next s, delim, start # copy start..end into result[curr-result] dest:text <- copy-range s, start, end *result <- put-index *result, curr-result, dest # slide over to next slice start <- add end, 1 curr-result <- add curr-result, 1 loop } ] scenario text-split-1 [ local-scope x:text <- new [a/b] run [ y:&:@:text <- split x, 47/slash 10:num/raw <- length *y a:text <- index *y, 0 b:text <- index *y, 1 20:@:char/raw <- copy *a 30:@:char/raw <- copy *b ] memory-should-contain [ 10 <- 2 # length of result 20:array:character <- [a] 30:array:character <- [b] ] ] scenario text-split-2 [ local-scope x:text <- new [a/b/c] run [ y:&:@:text <- split x, 47/slash 10:num/raw <- length *y a:text <- index *y, 0 b:text <- index *y, 1 c:text <- index *y, 2 20:@:char/raw <- copy *a 30:@:char/raw <- copy *b 40:@:char/raw <- copy *c ] memory-should-contain [ 10 <- 3 # length of result 20:array:character <- [a] 30:array:character <- [b] 40:array:character <- [c] ] ] scenario text-split-missing [ local-scope x:text <- new [abc] run [ y:&:@:text <- split x, 47/slash 10:num/raw <- length *y a:text <- index *y, 0 20:@:char/raw <- copy *a ] memory-should-contain [ 10 <- 1 # length of result 20:array:character <- [abc] ] ] scenario text-split-empty [ local-scope x:text <- new [] run [ y:&:@:text <- split x, 47/slash 10:num/raw <- length *y ] memory-should-contain [ 10 <- 0 # empty result ] ] scenario text-split-empty-piece [ local-scope x:text <- new [a/b//c] run [ y:&:@:text <- split x:text, 47/slash 10:num/raw <- length *y a:text <- index *y, 0 b:text <- index *y, 1 c:text <- index *y, 2 d:text <- index *y, 3 20:@:char/raw <- copy *a 30:@:char/raw <- copy *b 40:@:char/raw <- copy *c 50:@:char/raw <- copy *d ] memory-should-contain [ 10 <- 4 # length of result 20:array:character <- [a] 30:array:character <- [b] 40:array:character <- [] 50:array:character <- [c] ] ] def split-first text:text, delim:char -> x:text, y:text [ local-scope load-inputs # empty text? return empty texts len:num <- length *text { empty?:bool <- equal len, 0 break-unless empty? x:text <- new [] y:text <- new [] return } idx:num <- find-next text, delim, 0 x:text <- copy-range text, 0, idx idx <- add idx, 1 y:text <- copy-range text, idx, len ] scenario text-split-first [ local-scope x:text <- new [a/b] run [ y:text, z:text <- split-first x, 47/slash 10:@:char/raw <- copy *y 20:@:char/raw <- copy *z ] memory-should-contain [ 10:array:character <- [a] 20:array:character <- [b] ] ] def copy-range buf:text, start:num, end:num -> result:text [ local-scope load-inputs # if end is out of bounds, trim it len:num <- length *buf end:num <- min len, end # allocate space for result len <- subtract end, start result:text <- new character:type, len # copy start..end into result[curr-result] src-idx:num <- copy start dest-idx:num <- copy 0 { done?:bool <- greater-or-equal src-idx, end break-if done? src:char <- index *buf, src-idx *result <- put-index *result, dest-idx, src src-idx <- add src-idx, 1 dest-idx <- add dest-idx, 1 loop } ] scenario copy-range-works [ local-scope x:text <- new [abc] run [ y:text <- copy-range x, 1, 3 1:@:char/raw <- copy *y ] memory-should-contain [ 1:array:character <- [bc] ] ] scenario copy-range-out-of-bounds [ local-scope x:text <- new [abc] run [ y:text <- copy-range x, 2, 4 1:@:char/raw <- copy *y ] memory-should-contain [ 1:array:character <- [c] ] ] scenario copy-range-out-of-bounds-2 [ local-scope x:text <- new [abc] run [ y:text <- copy-range x, 3, 3 1:@:char/raw <- copy *y ] memory-should-contain [ 1:array:character <- [] ] ] def parse-whole-number in:text -> out:num, error?:bool [ local-scope load-inputs out <- copy 0 result:num <- copy 0 # temporary location i:num <- copy 0 len:num <- length *in { done?:bool <- greater-or-equal i, len break-if done? c:char <- index *in, i x:num <- character-to-code c digit:num, error?:bool <- character-code-to-digit x return-if error? result <- multiply result, 10 result <- add result, digit i <- add i, 1 loop } # no error; all digits were valid out <- copy result ] # (contributed by Ella Couch) recipe character-code-to-digit character-code:number -> result:number, error?:boolean [ local-scope load-inputs result <- copy 0 error? <- lesser-than character-code, 48 # '0' return-if error? error? <- greater-than character-code, 57 # '9' return-if error? result <- subtract character-code, 48 ] scenario character-code-to-digit-contain-only-digit [ local-scope a:number <- copy 48 # character code for '0' run [ 10:number/raw, 11:boolean/raw <- character-code-to-digit a ] memory-should-contain [ 10 <- 0 11 <- 0 # no error ] ] scenario character-code-to-digit-contain-only-digit-2 [ local-scope a:number <- copy 57 # character code for '9' run [ 1:number/raw, 2:boolean/raw <- character-code-to-digit a ] memory-should-contain [ 1 <- 9 2 <- 0 # no error ] ] scenario character-code-to-digit-handles-codes-lower-than-zero [ local-scope a:number <- copy 47 run [ 10:number/raw, 11:boolean/raw <- character-code-to-digit a ] memory-should-contain [ 10 <- 0 11 <- 1 # error ] ] scenario character-code-to-digit-handles-codes-larger-than-nine [ local-scope a:number <- copy 58 run [ 10:number/raw, 11:boolean/raw <- character-code-to-digit a ] memory-should-contain [ 10 <- 0 11 <- 1 # error ] ]