15 핵 낱말
(1) 핵 낱말(core word)은 use 없이 맨 이름으로 쓰는 낱말이다. 이 절의 표는 서명을 9.2 의 꼴로 적는다. T 는 “어느 타입이든 하나” 를 뜻하며 내장 낱말의 서명에만 쓰는 표기다. 스택 낱말(dup drop swap over nip)은 9.3 이, 줄기를 쓰는 조합 낱말은 10.6 이 정한다.
15.1 정수
(1) 산술. 넘치거나 0 으로 나누면 결함이다(11.3).
| 낱말 | 서명 | 뜻 |
add sub mul | (int, int gives int) | 더하기, 빼기, 곱하기 |
div mod | (int, int gives int) | 0 쪽으로 버리는 몫과 그 나머지(나머지는 나뉘는 수의 부호) |
floor_div floor_mod | (int, int gives int) | 아래로 버리는 몫과 그 나머지(나머지는 나누는 수의 부호) |
neg abs | (int gives int) | 부호 바꾸기, 절댓값. 가장 작은 int 에서는 넘침 |
min max | (int, int gives int) | 작은 쪽, 큰 쪽 |
clamp | (n int, lo int, hi int gives int) | n 을 lo 와 hi 사이로 |
rescale | (d dec(N), M gives dec(M)) | 점 아래 M 자리의 소수로(M 은 상수). 자리가 줄면 0 에서 먼 쪽으로 반올림(7.5) |
(2) 넘침을 다르게 다루는 낱말.
| 낱말 | 서명 | 뜻 |
wrap_add wrap_sub wrap_mul | (int, int gives int) | 264 를 법으로 감는다 |
sat_add sat_sub sat_mul | (int, int gives int) | 범위의 끝에서 멈춘다 |
chk_add chk_sub chk_mul chk_div | (int, int gives int or fail) | 넘치면 err.overflow, 0 으로 나누면 err.div_zero 로 실패 |
(3) 부호 없는 수로 보는 낱말. 64 비트를 0 부터 264−1 까지의 수로 읽는다.
| 낱말 | 서명 | 뜻 |
u_div u_mod | (int, int gives int) | 부호 없는 몫과 나머지. 0 으로 나누면 결함 |
u_lt u_le u_gt u_ge | (int, int gives bool) | 부호 없는 비교 |
mul_hi u_mul_hi | (int, int gives int) | 128 비트 곱의 위 64 비트(부호 있음, 없음) |
(4) 비트. 시프트와 회전의 수는 64 로 나눈 나머지(아래 6 비트)를 쓴다. shl 은 넘침을 검사하지 않는다.
| 낱말 | 서명 | 뜻 |
bit_and bit_or bit_xor | (int, int gives int) | 비트끼리 |
bit_not | (int gives int) | 모든 비트를 뒤집음 |
shl shr u_shr | (n int, k int gives int) | 왼쪽, 부호 있는 오른쪽, 0 을 채우는 오른쪽 |
rotl rotr | (n int, k int gives int) | 회전 |
count_ones leading_zeros trailing_zeros | (int gives int) | 1 인 비트의 수, 앞과 뒤의 0 의 수(0 이면 64) |
byte_swap | (int gives int) | 8 바이트의 차례를 뒤집음 |
(5) 좁은 정수(7.6)에서 위 낱말은 같은 타입 둘을 받아 그 타입을 준다. 넘침과 감기와 멈춤의 기준은 그 타입의 범위다. 부호 없는 수로 보는 낱말과 floor_div floor_mod 는 부호 없는 타입에서 보통 낱말과 같고 부호 있는 타입은 받지 않는다. 비트 낱말은 폭 안에서 한다. neg abs min max clamp 는 받지 않고, rotl rotr mul_hi u_mul_hi 는 u64 만 받는다.
15-integers.pnx
use io
word p (s str, n int) cat (s, " = ", n) io.println end
"7 2 floor_div" 7 2 floor_div p
"-7 2 floor_mod" -7 2 floor_mod p
"-1 2 u_div" -1 2 u_div p
"4611686018427387904 4 mul_hi" 4611686018427387904 4 mul_hi p
"12 10 bit_xor" 12 10 bit_xor p
"1 3 shl" 1 3 shl p
"1 64 shl" 1 64 shl p rem the count is taken modulo 64
"-16 2 shr" -16 2 shr p rem shr keeps the sign
"-16 60 u_shr" -16 60 u_shr p rem u_shr fills with zeros
"1 1 rotr" 1 1 rotr p
"255 count_ones" 255 count_ones p
"0 leading_zeros" 0 leading_zeros p
"1 byte_swap" 1 byte_swap p
"15 0 10 clamp" 15 0 10 clamp p
"false 1 2 select" false 1 2 select p
"9223372036854775807 2 sat_mul" 9223372036854775807 2 sat_mul p
"9223372036854775807 2 wrap_mul" 9223372036854775807 2 wrap_mul p
출력
$ pnx 15-integers.pnx
7 2 floor_div = 3
-7 2 floor_mod = 1
-1 2 u_div = 9223372036854775807
4611686018427387904 4 mul_hi = 1
12 10 bit_xor = 6
1 3 shl = 8
1 64 shl = 1
-16 2 shr = -4
-16 60 u_shr = 15
1 1 rotr = -9223372036854775808
255 count_ones = 8
0 leading_zeros = 64
1 byte_swap = 72057594037927936
15 0 10 clamp = 10
false 1 2 select = 2
9223372036854775807 2 sat_mul = 9223372036854775807
9223372036854775807 2 wrap_mul = -2
제약
(5) 정수 낱말은 int(또는 같은 선언한 타입, 7.4)만 받는다.
15-max-str.pnx
"a" "b" max drop
거절
$ pnx 15-max-str.pnx
15-max-str.pnx:1:9: this word takes two int values, or two of the same declared type
결함
(6) neg 과 abs 는 −263 에서 넘친다.
15-neg-min.pnx
-9223372036854775808 neg drop
결함으로 멈춤
$ pnx 15-neg-min.pnx
15-neg-min.pnx:1: integer overflow
15.2 비교와 논리
(1)
| 낱말 | 서명 | 뜻 |
eq ne | (T, T gives bool) | 같다, 다르다. int bool str 선언한 타입, 줄, 오류 번호 |
lt le gt ge | (T, T gives bool) | int(부호 있는 수로), str(바이트 차례로), 선언한 타입 |
and or | (bool, bool gives bool) | 논리곱, 논리합. 두 값을 모두 계산한다 |
not | (bool gives bool) | 부정 |
select | (bool, T, T gives T) | 참이면 앞의 값, 거짓이면 뒤의 값. 분기하지 않는다 |
15-compare.pnx
use io
word yes (b bool gives str) b if "true" else "false" end end
"abc" "abc" eq yes io.println rem strings compare by their bytes
"abc" "abd" lt yes io.println
-1 1 u_lt yes io.println rem as unsigned, -1 is the largest
true false ne yes io.println
출력
$ pnx 15-compare.pnx
true
true
false
true
제약
(2) 비교하는 두 값은 타입이 같아야 한다. select 의 두 값도 그렇다.
15-compare-mixed.pnx
1 true eq drop
거절
$ pnx 15-compare-mixed.pnx
15-compare-mixed.pnx:1:8: the two values compared must have the same type
15-select-types.pnx
true 1 "one" select drop
거절
$ pnx 15-select-types.pnx
15-select-types.pnx:1:14: select takes a bool and two values of one type: flag a b select
15.3 모음
(1) 8절이 선언하는 저장소를 다루는 낱말이다. c 는 그 저장소의 이름이다.
| 낱말 | 쓰는 곳 | 뜻 |
c len | 배열, 목록, 큐, 버퍼 | 지금 담긴 수(배열은 용량, 버퍼는 바이트 수). 풀은 줄을 세지 않는다 |
c capacity | 배열, 목록, 큐, 버퍼 | 선언한 용량 |
c is_empty | 배열, 목록, 큐, 버퍼 | 비었는가 |
c is_full | 배열, 목록, 큐 | 찼는가 |
c clear | 배열, 목록, 큐, 버퍼 | 비운다(배열은 모든 칸을 0 으로). 풀에는 쓰지 못한다: 줄을 하나씩 free 해서 비운다 |
i c idx | 배열, 목록 | 칸 i. 범위 밖이면 결함 range |
v c push / c pop | 목록 | 끝에 넣기 / 끝에서 빼기(or fail) |
c add_row | 레코드의 목록 | 빈 줄을 더하고 준다(or fail) |
v c put / c take / c peek | 큐 | 뒤에 넣기 / 앞에서 빼기 / 앞을 보기(or fail) |
v c wait_put / c wait_take | 큐 | 넣기 / 빼기, 큐가 찬 / 빈 동안은 양보(or fail, 14.3) |
c alloc / r c free / r c is_alive | 풀 | 줄 얻기(owned, or fail) / 돌려주기(찾아보기에서도 뺀다) / 살아 있는가 |
s c append n c append_int b c append_byte | 버퍼 | 붙이기(or fail) |
c contents | 버퍼 | 내용의 뷰 |
n c truncate | 버퍼, 목록 | 길이를 n 으로(목록은 8.5 의 (2b)) |
c none / r is_none / r row_index | 레코드의 표 | 없음 줄 / 없음인가 / 줄의 번호 |
k x lookup / r x insert / r x remove | 찾아보기 | 찾기(or fail) / 넣기(or fail) / 빼기 |
k x has | 찾아보기 | 키 k 의 줄이 있는가 |
c sort / c sort_desc | 배열, 목록 | 칸을 차례로 놓는다. 가장 작은 것 / 가장 큰 것부터((3)) |
o t sort_by r.f / o t sort_by_desc r.f | int 나 t 의 줄의 배열이나 목록, 레코드의 표(풀은 줄만) | 줄 번호나 줄을 그 줄의 필드 f 의 차례로 놓는다((4)) |
(2) 보기는 8절에 있다.
(3) c sort 는 배열의 칸을, 목록이면 길이까지의 칸을 가장 작은 것부터 차례로 놓는다. c sort_desc 는 가장 큰 것부터 놓는다. 칸은 제자리에서 바뀌고 낱말은 아무것도 주지 않는다. 칸은 int, 좁은 정수, dec(N), bool(false 가 먼저), str(N) 이거나, 그중 하나 위에 선언한 타입이어야 한다. u64 칸은 부호 없이 견주고, str(N) 칸은 str.compare 의 바이트 순서로 견준다(16.1). 모음 매개변수(9.9)도 똑같이 정렬한다. 레코드의 목록이나 배열은 거절된다. 찾아보기, 필드, 전역, 이름 붙은 줄이 그 줄을 붙들고 있을 수 있기 때문이다. 그 줄은 (4)로, 줄 번호의 목록을 거쳐 차례로 놓는다.
15-sort.pnx
use io
list marks int 6
list names str(8) 4
rem the cells on one line, a blank between two
word show (cells list int)
0 cells len range do (i int) i 0 gt if " " io.print end cat (i cells idx) io.print end each
"" io.println
end
word main (or fail)
7 marks push or_fail -2 marks push or_fail 12 marks push or_fail 0 marks push or_fail
marks sort
marks show
marks sort_desc
marks show
"pear" names push or_fail "Fig" names push or_fail "apple" names push or_fail
names sort
0 names len range do (i int) i 0 gt if " " io.print end i names idx io.print end each
"" io.println
end
main or_else (e) e err_text io.println end
출력
$ pnx 15-sort.pnx
-2 0 7 12
12 7 0 -2
Fig apple pear
15-sort-records.pnx
record entry
count int
end
list terms entry 8
word order_them () terms sort end
거절
$ pnx 15-sort-records.pnx
15-sort-records.pnx:5:26: rows do not move: sort a list of their numbers by a field: order table sort_by record.field
(4) o t sort_by r.f 는 o, t, 필드 r.f 를 받는다. o 는 int 의 배열이나 목록이고 칸마다 t 의 줄 번호다. 또는 t 의 줄을 담는 배열이나 목록이다(8.8 (5a)). t 는 레코드의 배열이나 목록이고, o 의 칸이 그 줄이면 레코드의 풀일 수도 있다. r.f 는 t 가 담는 레코드의 필드이고, to 와 set 이 이름을 받듯 낱말 뒤에 쓴다. 이 낱말은 o 의 칸을 그 칸이 가리키는 줄의 그 필드의 차례로, 가장 작은 것부터 놓는다. sort_by_desc 는 가장 큰 것부터 놓는다. 필드는 (3)의 칸이 될 수 있는 타입이어야 한다. 줄은 움직이지 않으므로 찾아보기와 붙들린 줄은 그대로 맞다. t 의 줄 번호가 아닌 칸과 없음 줄은 결함 range 이고, 그 사이 돌려준 풀의 줄은 결함 stale 이다. 둘 다 어느 칸도 움직이기 전에 찾는다. 그러면 o 는 그대로 남는다. o 와 t 가운데 하나만 매개변수일 수 있다.
15-sort-by.pnx
use io
record entry
term str(8)
count int
end
list terms entry 8
list order int 8
word count_in (t str, n int or fail)
terms add_row or_fail to r
t r to entry.term
n r to entry.count
end
word main (or fail)
"to" 4 count_in or_fail "be" 2 count_in or_fail "or" 2 count_in or_fail "not" 1 count_in or_fail "a" 4 count_in or_fail
0 terms len range do (i int) i order push or_fail end each
order terms sort_by entry.term
order terms sort_by_desc entry.count rem by count; the same count stays in byte order
0 order len range do (i int)
i order idx terms idx to r
cat (r entry.count, " ", r entry.term) io.println
end each
end
main or_else (e) e err_text io.println end
출력
$ pnx 15-sort-by.pnx
4 a
4 to
2 be
2 or
1 not
15-sort-by-range.pnx
record entry
count int
end
list terms entry 8
list order int 8
word main (or fail)
terms add_row or_fail to r
5 order push or_fail
order terms sort_by entry.count
end
main or_else (e) end
결함으로 멈춤
$ pnx 15-sort-by-range.pnx
15-sort-by-range.pnx:9: index out of range
r terms = 1
(5) 두 정렬은 모두 안정하다. 키가 같은 칸은 차례를 지킨다. 여러 키로 정렬하려면 여러 번 정렬한다. 덜 중요한 키부터 한다.
(6) 정렬은 실패하지 않는다. 정렬하는 적재는 프로그램에 작업 영역을 준다. 크기는 정렬하는 가장 큰 모음의 칸마다 8바이트이고, 모음 매개변수를 정렬하면 가장 큰 배열이나 목록의 칸마다 8바이트다. 작업 영역은 상태의 전역 하나로 세고, 블록에 그만한 자리가 있어야 한다. 칸의 정렬은 예산에 명령 개로 친다(19.5).
15.4 그 밖
(1)
| 낱말 | 서명 | 뜻 |
mark | (gives owned mark) | 구역의 지금 자리(12.2) |
release | (owned mark) | 구역을 그 자리로 되돌림(12.2) |
region_free | (gives int) | 구역에 남은 바이트 |
call | 블록의 서명대로 | 값이 되는 블록을 부름(10.5) |
raw | (D gives 바탕) | 선언한 타입의 값을 바탕 타입으로(7.4) |
err_text | (err gives str) | 오류 번호의 이름(11.1) |
assert | (bool) | 거짓이면 결함 failed |
spawn run_all resume is_done cancel yield task_tag | 14절 | 작업 |
진단
| 조건 | 메시지 |
|---|---|
정수 낱말에 int 가 아닌 값 | this word takes two int values |
| 비교하는 두 값의 타입이 다름 | the two values compared must have the same type |
select 의 값의 타입이 다름 | select takes a bool and two values of one type: flag a b select |
레코드의 배열이나 목록의 sort | rows do not move: sort a list of their numbers by a field: order table sort_by record.field |
다른 타입의 칸의 sort | sort orders cells of int, a narrow integer, dec(N), bool or str(N) |
int 나 줄의 목록과 표가 없는 sort_by | sort_by takes a list of rows or row numbers and a table of records: order table sort_by record.field |
| 다른 표의 줄을 담는 목록 | the cells are rows of another table than the one sort_by names |
뒤에 필드가 없는 sort_by | sort_by is followed by the field: order table sort_by record.field |
| 표의 레코드가 아닌 레코드의 필드 | the field is one of the record the table holds |
| 다른 타입의 필드 | sort_by orders by a field of int, a narrow integer, dec(N), bool or str(N) |
| 목록과 표가 모두 매개변수 | sort_by takes the list or the table from a parameter, not both |