17 표준 호스트 모듈
(1) 이 절의 모듈은 처리기가 함께 내는 호스트 낱말이다. 언어의 일부가 아니다 — 호스트가 등록할 때만 있다(13.3). pnx 는 여섯 모두를 등록한다. 라이브러리를 끼워 쓰는 호스트는 pinax_host_register(state, 처럼 모듈 하나씩 고른다(라이브러리
"io")libpinax_host). 등록하지 않은 모듈을 use 하면 거절된다(13.1).
(2) 호스트 낱말도 다른 낱말과 같은 규칙을 따른다: 서명은 적재 때 검사되고, or fail 이 있는 낱말은 지켜야 하며, owned 값은 정확히 한 번 넘긴다.
(3) 운영체제가 거절한 일은 실패(or fail)다. 없는 파일과 입력의 끝은 err.not_found, 바이트가 모양에 맞지 않으면 err.format, 자리가 모자라면 err.full, 그 밖의 거절(권한, 끊긴 장치)은 err.failed 다. or fail 이 없는 낱말(print, close)은 운영체제의 거절을 알리지 않는다.
17.1 io — 표준 입출력
(1)
| 낱말 | 서명 | 뜻 |
print println | (s str) | 표준 출력에 쓴다. println 은 줄바꿈(LF)을 붙인다 |
eprint | (s str) | 표준 오류에 쓴다 |
read_line | (gives line str or fail) | 표준 입력의 한 줄. 줄 끝은 뗀다. 입력이 끝나면 err.not_found |
read_line_into | (buf buffer or fail) | 한 줄을 버퍼에 붙인다 |
바이트는 고치지 않고 쓴다. 모든 운영체제에서 같은 바이트가 나간다.
17-io.pnx
use io
use str
word demo ()
"no new line, " io.print
"then one" io.println
loop
io.read_line or_else (e) break end to line rem fails at the end of the input
cat ("read: ", line, " (", line str.len, " bytes)") io.println
end
end
demo
출력
$ pnx 17-io.pnx
no new line, then one
read: first line (10 bytes)
read: second line (11 bytes)
17.2 file — 파일
(1) 핸들 타입 file 은 열린 파일이다. 여는 낱말이 owned file 을 주고, close 가 받는다(12.4).
| 낱말 | 서명 | 뜻 |
open_read open_write | (path str gives owned file or fail) | 읽기로, 쓰기로(새로 만들거나 비우고) 연다 |
write | (s str, f file or fail) | 바이트를 쓴다 |
read_line | (f file gives line str or fail) | 한 줄. 끝이면 err.not_found |
read | (f file, buf buffer gives n int or fail) | 버퍼에 읽어 붙이고 읽은 바이트 수를 준다 |
read_line_into | (f file, buf buffer or fail) | 한 줄을 버퍼에 붙인다 |
read_all | (path str gives body str or fail) | 파일 전체를 구역에 읽는다 |
read_all_into | (path str, buf buffer or fail) | 파일 전체를 버퍼에 붙인다 |
close | (f owned file) | 닫는다 |
없는 파일은 err.not_found 다.
17-file.pnx
use io
use file
buffer buf 64
word demo (or fail)
"data.txt" file.open_write or_fail to out
"alpha\nbeta\n" out file.write or_else (e) out file.close e fail end
out file.close
"data.txt" file.open_read or_fail to in
loop
in file.read_line or_else (e) break end to line
cat ("line: ", line) io.println
end
in file.close
"data.txt" buf file.read_all_into or_fail
cat ("all: ", buf contents) io.print
end
demo or_else (e) cat ("file error: ", e err_text) io.println end
"missing.txt" file.read_all or_else (e) cat ("missing.txt: ", e err_text) end io.println
출력
$ pnx 17-file.pnx
line: alpha
line: beta
all: alpha
beta
missing.txt: not_found
17.3 clock random env
(1)
| 낱말 | 서명 | 뜻 |
clock.now_ms | (gives int) | 지금 시각, 1970-01-01 UTC 부터의 밀리초 |
clock.monotonic_ns | (gives int) | 거꾸로 가지 않는 시계의 나노초 |
clock.sleep | (ms int) | 멈춰 기다린다. 그동안 상태는 다른 일을 하지 않는다 |
random.seed | (n int) | 수열을 정한다. 같은 씨앗은 같은 수열을 낸다 |
random.random | (gives int) | 64 비트 난수(암호용이 아님) |
random.random_below | (n int gives int) | 0 부터 n−1 까지 |
env.arg_count | (gives int) | 인자의 수. 0 번은 프로그램 파일 |
env.arg | (i int gives s str or fail) | 인자 i |
env.getenv | (name str gives s str or fail) | 환경 변수. 없으면 err.not_found |
17-env.pnx
use io
use env
cat ("arguments: ", env.arg_count) io.println
0 env.arg_count range do (i int) i env.arg or_else (e) "?" end io.println end each
"PINAX_SURELY_UNSET" env.getenv or_else (e) cat ("not set: ", e err_text) end io.println
출력
$ pnx 17-env.pnx alpha beta
arguments: 3
17-env.pnx
alpha
beta
not set: not_found
17-random-clock.pnx
use io
use random
use clock
word demo ()
42 random.seed rem the same seed, the same numbers
6 random.random_below to a
a 0 ge a 6 lt and if "a die in 0..5" io.println end
clock.monotonic_ns to t0
clock.monotonic_ns to t1
t1 t0 ge if "the monotonic clock does not go back" io.println end
end
demo
출력
$ pnx 17-random-clock.pnx
a die in 0..5
the monotonic clock does not go back
17.4 채널과 호스트 루프
(1) 채널(channel)은 바이트 메시지를 정한 수까지 담는 줄이다. 호스트가 채널에 이름을 짓고 상태에 그 이름의 모듈로 등록한다(pinax_channel_register, 라이브러리 libpinax_host). 같은 채널을 등록한 상태들이 메시지를 주고받는다. 상태끼리 함께 쓰는 메모리는 없다: 메시지는 들어갈 때와 나올 때 복사된다.
| 낱말 | 서명 | 뜻 |
send | (msg str or fail) | 메시지를 넣는다. 채널이 차 있으면 기다린다 |
receive | (gives msg str or fail) | 가장 먼저 들어온 메시지를 꺼낸다. 비어 있으면 기다린다 |
close | () | 더는 넣지 못하게 한다 |
(2) send 는 메시지가 채널이 정한 길이보다 길면 err.full, 채널이 닫힌 뒤에는 err.cancelled 로 실패한다. receive 는 닫히고 빈 채널에서 err.not_found 로 실패한다. 닫힌 채널에 남은 메시지는 그대로 꺼낸다.
(3) 기다리는 것은 부른 작업뿐이다(14.2). 같은 상태의 다른 작업은 계속 돈다. 기다릴 수 있는 것은 호스트 루프(pinax_loop_start)가 돌리는 실행뿐이다. 다른 실행에서 기다려야 하는 send 나 receive 는 결함 PINAX_ERR_STATE 로 실행을 끝낸다.
(4) 호스트 루프(host loop)는 상태들을 정한 수의 스레드에서 돌린다. pinax_loop_open 은 설정을 받는다: 스레드의 수, 한 번에 쥐는 실행의 수, 호스트 일의 결과가 차지할 수 있는 크기, 그리고 상태가 받듯 메모리 블록 하나. 루프의 객체는 그 안의 고정 칸이고(pinax_loop_size 바이트), 채널은 열릴 때 같은 블록에서 자기 칸을 잘라 간다 (pinax_channel_size 바이트). 실행이 도는 동안 할당은 없다. 블록이 없으면 루프가 그 크기의 조각 하나를 proven 의 힙에서 받는다. 한 상태는 한 번에 한 스레드에서만 돈다. 예산(19.5)이 다한 실행은 줄의 뒤로 가고, 기다리는 실행은 스레드를 내놓는다. 호스트가 pinax_loop_wait 로 기다리는 동안 남은 실행이 모두 채널을 기다리고 끝나지 않은 호스트 일이 없으면, 루프는 그 실행을 모두 PINAX_ERR_DEADLOCK 으로 끝낸다.
17.5 sh — 명령
(1) sh 는 시스템 셸(/bin/sh -c, Windows 에서는 cmd.exe /c)로 명령을 돌린다. pnx 는 이를 등록한다. 라이브러리를 넣는 호스트는 명령을 돌려도 되는 스크립트에만 등록한다(pinax_host_register(state, "sh"). pinax_host_register_all 은 등록하지 않는다). WebAssembly 호스트에는 sh 가 없다.
| 낱말 | 서명 | 뜻 |
sh.run | (cmd str gives status int or fail) | cmd 를 돌린다. 그 출력은 프로그램이 쓴 것 뒤에, 프로그램의 출력이 가는 곳으로 간다. 상태는 명령의 것이다: 0 이 아닌 상태는 실패가 아니다. 셸을 띄우지 못할 때만 실패한다 |
sh.output | (cmd str gives out str or fail) | cmd 를 돌리고, 그것이 표준 출력에 쓴 것을 쓴 그대로(줄바꿈과 함께) 영역에 준다 |
sh.output_into | (cmd str, buf buffer gives status int or fail) | 같은 것을 버퍼에 덧붙인다. 들어가지 않으면 err.full(명령은 끝까지 읽는다) |
sh.open_read | (cmd str gives owned file or fail) | 명령의 표준 출력에서 오는 관을 file(17.2)로 준다: file.read_line, file.read, file.close |
sh.open_write | (cmd str gives owned file or fail) | 명령의 표준 입력으로 가는 관: file.write, file.close |
sh.status | (gives int) | 마지막으로 끝난 명령의 상태: run, output, output_into, 또는 관의 file.close(명령이 끝나기를 기다린다) |
sh.quote | (s str gives str) | s 를 시스템 셸의 낱말 하나로 만든다. 자료로 명령을 만들 때 쓴다: /bin/sh 에는 작은따옴표로, cmd.exe 에는 큰따옴표로 싼다(거기서 % 와 ! 는 뜻을 지킨다) |
sh.cd | (path str or fail) | 프로세스의 작업 디렉터리를 바꾼다. 못 하면 err.not_found |
sh.cwd | (gives path str or fail) | 작업 디렉터리 |
sh.setenv | (name str, value str or fail) | 뒤따르는 명령(과 env.getenv)이 볼 환경 변수를 정한다 |
sh.exit | (status int) | 붙들린 것을 내보낸 뒤 그 상태(0 부터 255, 밖의 값은 잘린다)로 프로그램을 끝낸다. 실행은 거기서 멈추고, pnx repl 에서는 세션이 끝난다 |
17-sh.pnx
use io
use sh
word main (or fail)
"echo one; echo two" sh.output or_fail io.print
cat ("status ", "exit 4" sh.run or_fail) io.println
cat ("echo ", "it's here" sh.quote) sh.output or_fail io.print
end
main or_else (e) cat ("failed: ", e err_text) io.println end
출력
$ pnx 17-sh.pnx
one
two
status 4
it's here
(2) 상태는 명령의 종료 코드다. 신호로 끝난 명령은 128 에 신호의 번호를 더한 것이다. 찾지 못한 명령은 실패가 아니라 상태다(/bin/sh 에서 127). 0 바이트를 담은 명령은 err.argument 다. 읽기를 멈춘 명령으로 가는 관에 쓰면 실패한다 (err.failed). sh.status 와 sh.exit 는 상태를 돌리는 스레드의 것이다.