8 선언

8.1 일반

구문
declaration = [ "export" ] ( [ "bound" ] bindable | "shared" shareable | decl ) ;
bindable    = "global" NAME type
            | "array"  NAME type capacity
            | "buffer" NAME capacity ;
shareable   = bindable
            | "list"   NAME type capacity
            | "index"  NAME NAME PATH ;
decl        = "const"  NAME literal
            | "err"    NAME
            | "list"   NAME type capacity
            | "queue"  NAME type capacity
            | "pool"   NAME NAME capacity
            | "record" NAME { field | "big_endian" } "end"
            | "type"   NAME base
            | "index"  NAME NAME PATH
            | "tasks"  NAME capacity [ size [ size [ size ] ] ] [ "apart" ]
            | "extern" c-name "(" effect ")" "from" STRING [ "as" NAME ]
            | "test"   NAME { body } "end" ;
capacity    = size | "host" ;
size        = INT | NAME ;                         (* NAME: a const *)

(1) 선언(declaration)은 이름과 그 이름이 가리키는 것(값, 저장소, 타입)을 만든다. 선언은 정의 밖, 소스의 맨 바깥에만 쓴다.

(2) 처리기는 소스를 위에서 아래로 한 번 읽는다. 이름은 쓰는 자리보다 앞에서 선언돼 있어야 한다. 예외는 자기를 부르는 낱말과 forward(8.11)다.

(3) 한 모듈 안에서 같은 이름을 두 번 짓지 못한다. 종류가 달라도 그렇다. 핵 낱말과 타입의 이름도 다시 짓지 못한다.

(4) 용량(size)은 양의 정수 리터럴이거나 그런 값의 const 다. 용량은 적재 때 정해지고 돌 때 바뀌지 않는다. 모든 저장소는 적재 때 상태의 덩이 안에 자리를 잡는다.

(4a) 배열, 버퍼, 목록, 큐, 풀, 작업 묶음의 용량은 host 로 쓸 수 있다. 그러면 그것은 호스트가 적재 전에 그 선언에 준 수다(19.4 (1d)). 적재는 그 수를 거기에 적은 것처럼 다룬다. 호스트가 수를 주지 않은 선언에서 host 를 만난 적재는 거절되고, 메시지가 그 선언의 이름을 댄다. host 는 그 자리에서만 선언의 낱말이다. 그 자리에서는 같은 이름의 상수를 찾지 않는다.

08-host-capacity.pnx

tasks handlers host 32 8192 apart     rem as many as the host serves at once
list seen int host
word count (gives n int) seen len end

거절

$ pnx 08-host-capacity.pnx
08-host-capacity.pnx:1:16: the host gave no capacity for handlers (pinax_capacity)

(5) 전역, 배열의 칸, 목록·큐·풀의 칸, 버퍼, 레코드의 필드는 0 으로 시작한다(7 (3)).

제약

(6) 이미 있는 이름을 다시 지으면 거절된다.

08-name-twice.pnx

global total int
const total 5

거절

$ pnx 08-name-twice.pnx
08-name-twice.pnx:2:7: this name is already defined

(7) 선언보다 앞에서 이름을 쓰면 거절된다.

08-use-before.pnx

use str
word show (gives s str) total str.to_str end
global total int

거절

$ pnx 08-use-before.pnx
08-use-before.pnx:2:25: this name is not declared before this place

8.2 상수

(1) const 이름 리터럴 은 상수(constant)를 만든다. 타입은 리터럴의 타입이다. 상수는 저장소가 없다 — 쓰는 자리에 값이 들어간다. 용량, case 의 가지 값, row 의 값에 쓸 수 있다.

제약

(2) 상수에는 넣지 못한다. 정의 안에서 v to 상수 는 같은 이름의 지역 변수를 새로 만들려는 것이 되어 가림 금지(9.5)로 거절된다. 정의 밖에서는 to 가 지역을 만들지 않으므로 거절된다. set 은 전역에만 쓴다(9.4).

08-const-store.pnx

const limit 3
word raise () 4 to limit end

거절

$ pnx 08-const-store.pnx
08-const-store.pnx:2:20: this name is already visible here; a new name may not hide it

8.3 전역

(1) global 이름 타입 은 값 하나를 담는 전역(global)을 만든다. 타입은 int bool err dec(N) str(N), 선언한 타입, 줄의 타입(표의 이름), 핸들 타입이다. str 는 쓰지 못한다(7.3).

(2) 전역을 읽으려면 이름을 쓰고, 넣으려면 v set 이름 을 쓴다(9.4). 상태가 살아 있는 동안 값이 남는다 — 한 낱말의 실행이 끝나도, 다음 적재가 와도 그대로다.

08-const-global.pnx

use io
use str
const limit 3
const greeting "hello"
global hits int
global last str(16)
global seen bool
hits str.to_str io.println           rem a global starts at zero
limit set hits
greeting set last
true set seen
cat (last, " ", hits, " ", seen) io.println

출력

$ pnx 08-const-global.pnx
0
hello 3 true

8.4 배열

구문
assign-item = "assign" NAME "(" value { "," value } ")" ;

(1) array 이름 타입 용량 은 칸이 용량만큼 있는 배열(array)을 만든다. 배열은 늘 가득 차 있다: len 은 용량과 같다. 원소 타입은 int bool err dec(N) str(N), 좁은 정수(7.6), 선언한 타입, 레코드의 이름, 또는 표의 이름이다. 표의 이름이면 칸은 그 표의 줄을 담는다(8.8 (5a)). 좁은 정수의 칸은 그 폭만큼의 바이트이고, 다른 값의 칸은 8 바이트다. 목록과 큐의 칸도 같다.

(2) i 배열 idx 는 칸 i 의 값을 읽는다. v i set 배열 은 칸 i 에 넣는다. 모음 매개변수(9.9)의 칸도 같은 꼴로 넣는다. 번호는 0 부터 센다. 레코드의 배열에서 idx 는 값이 아니라 줄(8.8)을 낸다.

(3) assign 배열 (값, …) 은 정의 밖에서 배열을 0 번 칸부터 채운다. 값이 칸보다 적으면 나머지 칸은 그대로다. 같은 배열에 다시 쓰면 0 번 칸부터 다시 넣는다. 자료 적재(13.4)에서도 쓸 수 있다.

(4) 배열의 이름을 줄기로 쓰면 칸의 값을 0 번부터 차례로 낸다(10.6).

08-array.pnx

use io
use str
array primes int 6
assign primes (2, 3, 5, 7)                rem cells 4 and 5 stay 0
11 4 set primes                            rem v i to array: store into cell i
primes do (p int) p str.to_str io.print " " io.print end each
"" io.println
cat ("len ", primes len, ", cell 2 is ", 2 primes idx) io.println

출력

$ pnx 08-array.pnx
2 3 5 7 11 0 
len 6, cell 2 is 5
제약

(5) assign 의 값이 칸보다 많으면 거절된다.

08-assign-many.pnx

array small int 2
assign small (1, 2, 3)

거절

$ pnx 08-assign-many.pnx
08-assign-many.pnx:2:21: assign has more values than the array has cells
결함

(6) 번호가 0 보다 작거나 용량과 같거나 크면 결함 range 다.

08-array-range.pnx

use io
array primes int 4
"reading cell 4 of 4" io.println
4 primes idx drop

결함으로 멈춤

$ pnx 08-array-range.pnx
reading cell 4 of 4
08-array-range.pnx:4: index out of range

8.5 목록

(1) list 이름 타입 용량 은 길이가 0 에서 용량까지 자라는 목록(list)을 만든다. 칸을 읽고 쓰는 것은 배열과 같으나, 번호는 길이 안에서만 쓴다.

(2) v 목록 push 는 끝에 하나를 더하고, 목록 pop 은 끝의 하나를 빼서 준다. 둘은 실패할 수 있다 (or fail): 가득 찬 목록에 push 하면 err.full, 빈 목록에서 pop 하면 err.range 다. str(N) 목록에 너무 긴 글을 push 하면 err.range 다(7.3 (4a)). 레코드의 목록에는 목록 add_row 로 빈 줄을 더하고 그 줄을 받는다. 목록 clear 는 길이를 0 으로 한다.

08-list.pnx

use io
use str
list stack int 3
word fill (or fail)
  1 stack push or_fail
  2 stack push or_fail
end
fill or_else (e) "fill failed" io.println end
cat ("len ", stack len, " of ", stack capacity) io.println
stack pop or_else (e) -1 end str.to_str io.println
cat ("len ", stack len) io.println

출력

$ pnx 08-list.pnx
len 2 of 3
2
len 1

08-list-full.pnx

use io
list two int 2
1 two push or_else (e) end
2 two push or_else (e) end
3 two push or_else (e) cat ("third push failed: ", e err_text) io.println end

출력

$ pnx 08-list-full.pnx
third push failed: full

(2a) n 목록 truncate 는 길이를 n 으로 줄인다. 정수, 글, 레코드의 목록 모두에 쓴다. add_row 로 더한 줄의 값이 틀린 것을 알았을 때 그 줄을 되돌리는 데 쓴다.

08-list-truncate.pnx

use io
record city
  name str(16)
  people int
end
list cities city 4
rem Add a row, and take it back when its values turn out wrong.
word add_city (name str, people int or fail)
  cities len to before
  cities add_row or_fail to c
  name c to city.name
  people 0 lt if before cities truncate err.format fail end
  people c to city.people
end
"Seoul" 9411000 add_city or_else (e) end
"Nowhere" -1 add_city or_else (e) cat ("refused: ", e err_text) io.println end
cat (cities len, " rows") io.println

출력

$ pnx 08-list-truncate.pnx
refused: format
1 rows
제약

(2b) 줄이 다른 곳에 남는 레코드의 목록은 truncate 하지 못한다. 그 목록의 줄을 담는 필드나 전역이나 모음, 그 목록에 row 로 이름 붙인 줄, 그 목록의 찾아보기(8.10)가 같은 소스 안에 하나라도 있으면 거절된다. 잘라 낸 자리에 새 줄이 들어서면 그것들이 엉뚱한 줄을 가리키기 때문이다. 지역에 든 줄은 쓸 때 검사한다(결함 range).

08-list-truncate-kept.pnx

record city
  name str(16)
end
list cities city 4
index by_name cities city.name
word forget () 0 cities truncate end

거절

$ pnx 08-list-truncate-kept.pnx
08-list-truncate-kept.pnx:6:25: this list's rows are kept by a field, a global, a collection, a named row or an index; it cannot be truncated
결함

(3) 길이 밖의 번호는 용량 안이라도 결함 range 다. truncate 에 길이보다 큰 수나 음수를 주어도 결함 range 다.

08-list-past-len.pnx

list items int 8
1 items push or_else (e) end
1 items idx drop                          rem the list holds one value: index 1 is past it

결함으로 멈춤

$ pnx 08-list-past-len.pnx
08-list-past-len.pnx:3: index out of range
진단
조건메시지
줄이 남는 목록을 truncatethis list's rows are kept by a field, a global, a collection, a named row or an index; it cannot be truncated

8.6 큐

(1) queue 이름 타입 용량 은 먼저 넣은 것이 먼저 나오는 큐(queue)를 만든다. 원소 타입은 int bool err dec(N) 이다. v 큐 put 은 뒤에 넣고, 큐 take 는 앞에서 빼서 주며, 큐 peek 은 빼지 않고 앞의 것을 준다. 셋 다 실패할 수 있다: 가득 찬 큐에 넣으면 err.full, 빈 큐에서 꺼내면 err.range 다.

08-queue.pnx

use io
use str
queue jobs int 4
word show (gives s str or fail) jobs take or_fail str.to_str end
10 jobs put or_else (e) end
20 jobs put or_else (e) end
jobs peek or_else (e) 0 end str.to_str io.println
show or_else (e) "?" end io.println
show or_else (e) "?" end io.println
show or_else (e) cat ("empty: ", e err_text) end io.println

출력

$ pnx 08-queue.pnx
10
10
20
empty: range

8.7 버퍼

(1) buffer 이름 용량 은 용량만큼의 바이트를 담는 버퍼(buffer)를 만든다. 버퍼는 고칠 수 있는 바이트의 열이다. 글을 조금씩 이어 붙일 때 쓴다.

(2) s 버퍼 append, n 버퍼 append_int(십진; n 은 int 나 좁은 정수), b 버퍼 append_byte(b 는 u8) 는 끝에 붙이고, 자리가 모자라면 err.full 로 실패한다(실패하면 아무것도 붙이지 않는다). 버퍼 contents 는 지금 내용의 str 뷰를 준다. n 버퍼 truncate 는 길이를 n 으로 줄인다. 버퍼 clear 는 길이를 0 으로 한다.

08-buffer.pnx

use io
buffer out 32
word build (or fail)
  "x=" out append or_fail
  42 out append_int or_fail
  '!' out append_byte or_fail
end
build or_else (e) "full" io.println end
out contents io.println
2 out truncate
out contents io.println

출력

$ pnx 08-buffer.pnx
x=42!
x=

(3) contents 가 준 뷰는 버퍼의 바이트를 가리킨다. 버퍼를 고치면 뷰가 보는 바이트도 바뀐다.

8.8 레코드와 줄

구문
record   = "record" NAME { field | "big_endian" } "end" ;
field    = NAME width ;
width    = "u8" | "u16" | "u32" | "u64" | "i8" | "i16" | "i32" | "i64"
         | "int" | "bool" | "varint" | "dec" "(" INT ")" | "str" "(" size ")"
         | NAME ;                                         (* NAME: a table *)
row-item = [ "export" ] "row" NAME [ NAME ] "(" value { "," value } ")" ;
value    = literal | NAME ;                         (* NAME: a const, a row name, none *)

(1) record 이름 … end 는 필드의 나열인 레코드(record) 타입을 만든다. 필드마다 폭을 적는다. int 와 i64 필드는 읽으면 int, u8 … i32 필드는 그 좁은 정수(7.6), bool 필드는 bool, dec(N) 필드는 dec(N)(8 바이트), str(N) 필드는 str 뷰가 된다. 필드에는 그 타입의 값만 넣는다. 필드의 타입에 표의 이름을 쓰면 그 표의 줄을 담는다.

(2) 레코드는 값으로 다루지 않는다. 레코드를 원소로 하는 배열·목록·풀(표(table))을 선언하고, 그 한 칸인 줄(row)을 다룬다. 줄의 타입은 표의 이름이다. 줄은 표의 번호 하나를 담고, 쓸 때마다 범위를 검사한다(풀이면 세대도, 8.9).

(3) 필드는 경로 레코드.필드 로 적는다. 줄 레코드.필드 는 읽고, v 줄 to 레코드.필드 는 넣는다.

(4) row 표 (값, …) 는 정의 밖에서 목록에 줄 하나를 더한다. 값은 필드의 차례로 적고, 뒤쪽 필드는 줄일 수 있다(0 이 된다). row 표 이름 (…) 은 그 줄에 이름을 붙인다. 줄의 이름은 뒤의 row 값과 코드에서 그 줄로 쓴다.

08-record.pnx

use io
record user
  name str(16)
  age u8
  admin bool
end
list users user 8
row users ("kim", 30, true)
row users lee ("lee", 25)                  rem a named row; the fields left out stay zero
word show (u users) cat (u user.name, " ", u user.age, " ", u user.admin) io.println end
users do (u users) u show end each
26 u8 lee to user.age                         rem v row to record.field
lee show

출력

$ pnx 08-record.pnx
kim 30 true
lee 25 false
lee 26 false

(5) 줄을 담는 필드와 전역의 처음 값은 “없음” 이다. 줄 is_none 은 없음인지 묻고, 표 none 은 그 표의 없음 줄을 낸다. 줄 row_index 는 줄의 번호를 준다.

(5a) 원소 타입이 표의 이름인 배열이나 목록은 그 표의 줄을 8 바이트 칸에 담는다. 칸에는 줄의 번호에 1 을 더한 값이 들고, 풀의 줄이면 그 세대가 위쪽 32 비트에 든다. 0 은 없음이고, 칸은 모두 없음으로 시작한다. 칸은 다른 칸처럼 읽고, 넣고, push·pop 하고, 돌며, 들어가는 값은 그 표의 줄이어야 한다. 칸에서 읽은 줄은 다른 줄처럼 쓰는 자리에서 검사한다. 큐는 줄을 담지 않는다. alloc 이 막 준 줄(8.9)은 set 으로 칸에 넣는다. set 은 필드처럼 그 줄을 넘겨받는다. push 는 그 줄을 거부한다. 실패한 push 는 그 줄을 잃기 때문이다.

08-row-cells.pnx

use io
record city
  name str(12)
  people int
end
list cities city 4
list big cities 4                          rem rows of cities
row cities ("Oslo", 709)
row cities ("Bergen", 291)
row cities ("Tromso", 78)
cities do (c cities gives bool) c city.people 100 gt end big filter_into or_else (e) end
big cities sort_by city.name
big do (c cities) c city.name io.println end each

출력

$ pnx 08-row-cells.pnx
Bergen
Oslo

(6) big_endian 은 16.5 의 bytes.pack 과 bytes.unpack 이 쓰는 바이트 순서만 정한다. 메모리 안의 모양은 바꾸지 않는다.

제약

(7) row 의 값은 필드의 차례와 타입에 맞아야 한다.

08-row-types.pnx

record user
  name str(16)
  age u8
end
list users user 8
row users (30, "kim")

거절

$ pnx 08-row-types.pnx
08-row-types.pnx:6:12: a value has another type than its field
결함

(8) 좁은 정수 필드에 넣을 int 는 먼저 그 타입으로 바꾼다(n u8); 값이 폭에 맞지 않으면 그 변환이 결함 overflow 다(7.6). 자르지 않는다 — 잘라 넣으려면 low_u8 처럼 아래 비트를 받는 낱말을 쓴다. 없음 줄의 필드를 읽거나 쓰면 결함 range 다.

08-record-narrow.pnx

record pixel
  level u8
end
array image pixel 4
word set_level (n int) n u8 0 image idx to pixel.level end     rem the conversion checks the range
300 set_level                                                   rem 300 does not fit in u8

결함으로 멈춤

$ pnx 08-record-narrow.pnx
08-record-narrow.pnx:5: integer overflow
  n int = 300

8.9 풀

(1) pool 이름 레코드 용량 은 줄을 얻고 돌려주는 풀(pool)을 만든다. 풀 alloc 은 빈 줄 하나를 얻고(or fail, 남은 줄이 없으면 err.full), 줄 풀 free 는 돌려준다. 줄 풀 is_alive 는 그 줄이 아직 살아 있는지 묻는다.

(2) 풀의 줄은 번호와 함께 세대(generation)를 담는다. 줄을 돌려주면 그 칸의 세대가 바뀐다. 그래서 돌려준 줄을 다시 쓰면 — 그 사이 같은 칸을 다른 줄이 얻었더라도 — 알아챈다.

(3) alloc 이 준 줄은 owned 값(12.4)이다. 모든 경로에서 정확히 한 번, 필드나 전역에 넣거나 free 에 줘야 한다. 넣은 뒤에는 세대 검사가 그 줄을 지킨다.

08-pool.pnx

use io
record node
  value int
end
pool nodes node 2
global kept nodes
word demo (or fail)
  nodes alloc or_fail to a                 rem an owned row: it must be stored or freed
  7 a to node.value
  a set kept                                rem stored: the global holds the row now
  cat ("value ", kept node.value, ", alive: ", kept nodes is_alive) io.println
  kept nodes free
  cat ("alive after free: ", kept nodes is_alive) io.println
end
demo or_else (e) "no room" io.println end

출력

$ pnx 08-pool.pnx
value 7, alive: true
alive after free: false

(3a) 풀 do (r 풀) … end each 는 풀에서 살아 있는 줄을 칸의 차례로 들른다. 얻은 차례가 아니다. 걷기가 닿기 전에 블록 안에서 돌려준 줄은 들르지 않는다. 블록 안에서 얻은 줄은 그 칸이 뒤에 있으면 들른다. 풀을 걷는 조합 낱말(10.6)은 each 하나뿐이다.

제약

(4) 얻은 줄을 넣지도 돌려주지도 않는 경로가 있으면 거절된다.

08-pool-leak.pnx

record node
  value int
end
pool nodes node 2
word leak (or fail)
  nodes alloc or_fail to a
  7 a to node.value
end

거절

$ pnx 08-pool-leak.pnx
08-pool-leak.pnx:8:1: an owned value in a local is not given on every path (close, free or store it)

(4a) 풀에 each 가 아닌 조합 낱말을 쓰면 거절된다.

08-pool-walk.pnx

record node
  value int
end
pool nodes node 4
word live (gives n int)
  nodes do (r nodes) true end count
end

거절

$ pnx 08-pool-walk.pnx
08-pool-walk.pnx:6:22: a pool is walked by each only
결함

(5) 돌려준 줄을 쓰면 결함 stale 이다. pnx 는 결함 아래에 그 낱말의 지역 변수를 함께 보인다.

08-pool-stale.pnx

record node
  value int
end
pool nodes node 2
global keep nodes
word demo (or fail)
  nodes alloc or_fail to a
  a set keep                                 rem stored: the row is handed over
  keep nodes free
  keep node.value drop                      rem the row was given back: stale
end
demo or_else (e) end

결함으로 멈춤

$ pnx 08-pool-stale.pnx
08-pool-stale.pnx:10: a row or handle was used after it was given back
  a nodes = 4294967297
진단
조건메시지
풀에 each 가 아닌 조합 낱말a pool is walked by each only

8.10 찾아보기

(1) index 이름 표 레코드.필드 는 레코드의 표(배열, 목록, 풀)를 한 필드로 찾는 찾아보기(index)를 만든다. 키 필드는 int 이거나 str(N) 이다. 찾아보기는 표의 용량에 맞춘 고정 크기의 해시 표이고 줄의 번호만 담는다. 자료는 표에 있다.

(2) 키 찾아보기 lookup 은 그 키의 줄을 준다(or fail, 없으면 err.not_found). 줄 찾아보기 insert 는 줄을 넣고(or fail, 같은 키가 이미 있으면 실패), 줄 찾아보기 remove 는 뺀다. 키 필드를 바꾸려면 빼고, 고치고, 다시 넣는다.

(2a) 키 찾아보기 has 는 그 키의 줄이 찾아보기에 있으면 true, 없으면 false 를 준다. 실패하지 않는다.

(3) 적재 때 row 로 적은 줄은 찾아보기에 들어 있다. 돌 때 add_row 로 더한 줄은 insert 해야 찾아진다.

08-index.pnx

use io
use str
record user
  name str(16)
  age u8
end
list users user 8
index by_name users user.name
row users ("kim", 30)
row users ("lee", 25)
word find_age (name str gives age int or fail) name by_name lookup or_fail user.age raw end
"lee" find_age or_else (e) -1 end str.to_str io.println
"park" find_age or_else (e) -1 end str.to_str io.println

출력

$ pnx 08-index.pnx
25
-1

08-index-insert.pnx

use io
use str
record user
  name str(16)
  age int
end
list users user 4
index by_name users user.name
word add_user (name str, age int, indexed bool or fail)
  users add_row or_fail to r
  name r to user.name
  age r to user.age
  indexed if r by_name insert or_fail end  rem a row added while running is indexed by insert
end
word age_of (name str gives int or fail) name by_name lookup or_fail user.age end
"kim" 30 true add_user or_else (e) end
"lee" 25 false add_user or_else (e) end
"kim" age_of or_else (e) -1 end str.to_str io.println
"lee" age_of or_else (e) -1 end str.to_str io.println     rem not inserted: not found

출력

$ pnx 08-index-insert.pnx
30
-1

(3a) 찾아보기의 표는 풀(8.9)일 수 있다. 그러면 lookup 은 줄을 지금 모습대로, 세대와 함께 준다. 돌려준 뒤의 줄을 insert 나 remove 에 주면 결함 stale 이다. free 는 먼저 그 풀 위의 모든 찾아보기에서 줄을 뺀다. alloc 이 방금 준 줄은 insert 에 넘겨준다(12.4). insert 가 실패하면 실패를 넘기기 전에 줄을 풀에 돌려준다.

08-index-pool.pnx

use io
record user
  name str(16)
  age int
end
pool users user 4
index by_name users user.name
word add_user (name str, age int or fail)
  users alloc or_fail to r
  name r to user.name
  age r to user.age
  r by_name insert or_fail                rem hands the row over to the index
end
word forget (name str)
  name by_name lookup or_else (e) return end to r
  r users free                            rem out of by_name too
end
"kim" 30 add_user or_else (e) end
"lee" 25 add_user or_else (e) end
"kim" 41 add_user or_else (e) cat ("kim again: ", e err_text) io.println end
cat ("kim: ", "kim" by_name has, ", park: ", "park" by_name has) io.println
"kim" forget
cat ("kim after free: ", "kim" by_name has) io.println
users do (r users) cat (r user.name, " ", r user.age) io.println end each

출력

$ pnx 08-index-pool.pnx
kim again: failed
kim: true, park: false
kim after free: false
lee 25

(4) 해시는 키가 있는 해시(SipHash-2-4)다. 키는 호스트가 준다(19.2). 밖에서 온 글을 키로 쓰는 호스트는 무작위 키를 줘야 한다 — 키를 아는 쪽은 충돌을 몰아 찾기를 느리게 만들 수 있다.

제약

(5) 풀 위의 찾아보기는 풀처럼 shared 가 아니다(8.13).

08-index-pool-shared.pnx

record user
  age int
end
pool users user 4
shared index by_age users user.age

거절

$ pnx 08-index-pool-shared.pnx
08-index-pool-shared.pnx:5:21: an index over a pool is not shared, as a pool is not
진단
조건메시지
레코드의 배열, 목록, 풀이 아닌 표an index belongs to a declared array, list or pool of records
풀 위의 찾아보기 앞의 sharedan index over a pool is not shared, as a pool is not
찾아보기가 아닌 것의 hashas takes a key and the name of an index: key by_name has

8.11 앞선 선언

구문
forward = "forward" ( "array" | "list" | "pool" ) NAME
        | "forward" "word" NAME "(" effect ")" ;

(1) forward list 이름 은 뒤에서 선언할 표를 미리 알린다. 레코드가 자기를 담을 표의 줄을 필드로 가질 때 쓴다. 미리 알린 표의 이름은 타입 자리에만 쓴다. forward word 는 9.7 이 정한다.

08-forward.pnx

use io
use str
forward list nodes                         rem the record below points into this list
record node
  value int
  next nodes
end
list nodes node 4
row nodes b (2)
row nodes a (1, b)
a node.next node.value str.to_str io.println
a node.next node.next is_none if "the chain ends at b" io.println end

출력

$ pnx 08-forward.pnx
2
the chain ends at b
제약

(2) 미리 알린 표는 같은 모듈에서 같은 종류로 선언돼야 한다.

08-forward-missing.pnx

forward list nodes
record node
  value int
end

거절

$ pnx 08-forward-missing.pnx
08-forward-missing.pnx:5:1: a table announced by forward is never declared

8.12 묶은 저장소

(1) bound 를 앞에 붙인 global array buffer 는 저장소를 호스트가 준다. 스크립트에서는 다른 전역과 같이 쓴다. 호스트가 묶기 전에는 상태 안의 자리를 쓴다. 묶는 법은 19.7 이 정한다.

8.13 공유 저장소

(1) shared 를 앞에 붙인 global array list buffer index 는 다른 상태의 저장소를 읽기만 한다. 선언한 상태에는 저장소가 없고, 호스트가 다른 상태의 같은 선언을 pinax_share 로 준다(19.7). 같은 자료를 여러 상태가 여러 스레드에서 한꺼번에 읽어도 복사도 잠금도 없다.

제약

(2) 공유 전역은 아무것도 바꾸지 못한다: 넣기, set, push, pop, clear, truncate, add_row, 필드에 넣기, insert, remove, 버퍼에 붙이는 낱말, map_into 와 filter_into 의 목적지가 모두 거절된다. 처리기는 공유 전역을 바꾸는 바이트코드도 적재 때 거절하고, 매개변수로 받은 공유 전역을 바꾸려는 바이트코드는 실행 때 결함 state 로 끝낸다.

08-shared-write.pnx

record city
  name str(12)
  people int
end
shared list cities city 1000
word grow (r cities) r city.people 1 add r to city.people end

거절

$ pnx 08-shared-write.pnx
08-shared-write.pnx:6:47: a shared global is read-only: nothing here may change it

(3) 공유 모음은 그 매개변수를 읽기만 하는 낱말의 모음 매개변수(9.9)로 넘길 수 있다. 낱말이 매개변수를 바꾸는 명령을 쓰거나, 매개변수를 바꾸는 낱말이나 호스트 낱말의 buffer 매개변수로 넘기면 그 매개변수를 바꾸는 낱말이다. 처리기는 원본을 다 읽은 뒤에 판단하므로 forward 로 알린 낱말에도, 앞선 적재의 낱말에도 같다.

08-shared-param.pnx

use io
use str
shared list scores int 100
word total (l list int gives int) l sum end       rem reads its parameter only
word everyone (gives int) scores total end        rem so a shared list may be given to it
scores total str.to_str io.println                rem not given yet: empty

출력

$ pnx 08-shared-param.pnx
0

08-shared-change.pnx

shared list scores int 100
word reset (l list int) l clear end
word keep (l list int) l reset end                rem gives it to a word that changes it
word start () scores keep end

거절

$ pnx 08-shared-change.pnx
08-shared-change.pnx:4:22: a shared collection is read-only: the word it is given to changes this parameter

(3a) 공유 모음은 호스트 낱말의 buffer 매개변수와 블록 값이 부르는 낱말(call)의 매개변수로는 넘기지 않는다. 받는 쪽이 무엇을 하는지 적재 때 모르기 때문이다. redefine 은 읽기만 하던 매개변수를 바꾸는 낱말로 만들지 못한다.

(4) shared 는 queue pool tasks 와 const record 앞에 오지 않는다.

08-shared-queue.pnx

shared queue jobs int 8

거절

$ pnx 08-shared-queue.pnx
08-shared-queue.pnx:1:8: shared stands before global, array, list, buffer or index
의미

(5) 호스트가 주기 전에는 비어 있다: 목록과 배열의 길이는 0 이고, 찾아보기는 아무것도 찾지 못하며, 스칼라와 버퍼는 0 과 빈 바이트다. 적재의 코드는 그대로 돈다. 호스트의 실행은 공유 전역을 모두 받을 때까지 거절된다(19.7).

8.14 실패 코드

(1) err 이름 은 실패 코드(failure code)를 만든다. err 타입의 새 오류 코드다(11.1). 상수(8.2)처럼 저장소가 없고, 그 이름은 제 모듈의 이름이다. 다른 모듈은 내보낸 것을 모듈.이름 으로 쓴다. 다른 코드를 쓰는 자리 어디에나 쓴다: fail 과 함께, or_else (e) 와 eq 로, case 의 가지 값으로, err 전역이나 모음 안에. err_text 는 그 이름을 준다.

(2) 상태가 선언한 실패 코드는 선언된 차례대로 0x400 부터 번호가 붙는다(19.1). 프로그램은 번호에 기대지 않는다. 호스트는 이름을 읽는다(pinax_error_text).

08-err.pnx

use io
use str
err key_too_long
err value_too_long
word check (k str, v str or fail)
  k str.len 8 gt if key_too_long fail end
  v str.len 16 gt if value_too_long fail end
end
word try (k str, v str)
  k v check or_else (e)
    e case
      key_too_long of "the key is too long" end
      value_too_long of "the value is too long" end
      otherwise e err_text end
    end io.println
    return
  end
  "stored" io.println
end
"name" "value" try
"a long key name" "value" try
"name" "a value of thirty bytes, more" try
key_too_long err_text io.println

출력

$ pnx 08-err.pnx
stored
the key is too long
the value is too long
key_too_long
제약

(3) err 뒤에는 31 바이트 이하의 이름이 온다. 그 이름은 8.1 (3)과 (6)을 따른다.

08-err-name.pnx

err "too long"

거절

$ pnx 08-err-name.pnx
08-err-name.pnx:1:5: err is followed by the name of a new failure code
진단
조건메시지
이미 있는 이름this name is already defined
선언 앞에서 쓴 이름this name is not declared before this place
호스트가 수를 주지 않은 host 용량(메시지에 선언의 이름)the host gave no capacity for NAME (pinax_capacity)
정의 밖의 to 가 전역이 아닌 이름에outside a definition `to` makes no new local; a global is written with set
assign 의 값이 칸보다 많음assign has more values than the array has cells
큐의 원소 타입이 int bool err dec(N) 밖a queue holds int, bool, err or dec(N) values
alloc 이 막 준 줄의 push(8.8 (5a))push can fail and lose an owned row; store it with set, into a field or into a global
row 의 값이 필드와 다름a value has another type than its field
owned 줄을 넘기지 않는 경로an owned value in a local is not given on every path (close, free or store it)
미리 알린 표가 없음a table announced by forward is never declared
공유 전역을 바꿈a shared global is read-only: nothing here may change it
공유 모음을 그것을 바꾸는 낱말에 넘김a shared collection is read-only: the word it is given to changes this parameter
공유 모음을 블록 값의 낱말에 넘김a shared collection is read-only: it is not given to the word a block value names, which may change it
redefine 이 읽던 매개변수를 바꿈redefine cannot make the word change a collection parameter the old one only read: shared collections may have been given to it
공유 버퍼를 호스트 낱말에 넘김a shared buffer is read-only: it is not given to a host word, which may write it
shared 뒤에 다른 선언shared stands before global, array, list, buffer or index
이름이 따르지 않는 errerr is followed by the name of a new failure code
31 바이트를 넘는 실패 코드의 이름the name of a failure code is at most 31 bytes long