18 C 함수 부르기

구문
extern = [ "export" ] "extern" c-name "(" effect ")" "from" STRING [ "as" NAME ] ;
c-name = NAME | reserved-word | STRING ;

(1) extern 이름 (서명) from "라이브러리" 는 C 함수 하나를 낱말로 삼는다. 처리기는 라이브러리를 스스로 열지 않는다. 적재할 때 호스트의 해결 함수(resolver)에 (라이브러리, 이름)을 묻고, 호스트가 준 함수를 부른다. 호스트가 해결 함수를 두지 않거나 함수를 주지 않으면 적재가 거절된다. 그래서 스크립트가 부를 수 있는 C 함수는 호스트가 정한다.

(2) 끝에 as 다른이름 을 붙이면 스크립트는 그 이름으로 부르고, 호스트에게 묻는 이름은 그대로다. C 함수의 이름이 핵 낱말이나 예약 낱말과 같거나 이름의 글자가 아닌 글자를 담으면(따옴표로 적는다) as 가 있어야 한다.

(3) 서명에 쓸 수 있는 것: 인자는 int(그리고 bool, 좁은 정수(7.6), 선언한 타입처럼 int 로 표현되는 값), 핸들, str, cstr. 결과는 int 나 핸들 하나이거나 없다. or fail 은 핸들 결과에만 붙이며, 함수가 널 포인터를 주면 실패다. cstr 는 이 서명에만 쓰는 타입이다.

(4) 부르는 방식: int 와 핸들의 객체 포인터는 intptr_t 인자 하나씩, str 는 (const char *, size_t) 두 인자(NUL 로 끝나지 않는다), cstr 는 NUL 로 끝나는 복사본의 const char * 하나다. C 인자는 여덟까지다. 결과는 intptr_t 하나로 읽는다. 다른 모양의 함수(실수 인자, int 결과, 구조체, 가변 인자)는 호스트 낱말로 감싼다.

18-extern.c

#include "pinax.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>

/* Two C functions the script names. Every integer is an intptr_t; a str is (pointer, length). */
static intptr_t gcd(intptr_t a, intptr_t b) {
    while (b) { intptr_t t = a % b; a = b; b = t; }
    return a;
}
static intptr_t count_vowels(const char *s, size_t n) {
    intptr_t k = 0;
    for (size_t i = 0; i < n; ++i)
        k += strchr("aeiou", s[i]) != NULL && s[i] != '\0';
    return k;
}

/* The host decides what "mathlib" means: here, a table of its own functions. */
static pinax_extern_fn resolve(void *user, pinax_str library, pinax_str symbol) {
    (void)user;
    if (library.len != 7 || memcmp(library.ptr, "mathlib", 7) != 0)
        return NULL;
    if (symbol.len == 3 && memcmp(symbol.ptr, "gcd", 3) == 0)
        return (pinax_extern_fn)gcd;
    if (symbol.len == 12 && memcmp(symbol.ptr, "count_vowels", 12) == 0)
        return (pinax_extern_fn)count_vowels;
    return NULL;
}

static void try_load(pinax_state *st, const char *name, const char *text) {
    if (pinax_load(st, name, (pinax_str){(const unsigned char *)text, strlen(text)}, PINAX_LOAD_CODE) != PINAX_OK) {
        pinax_str why = pinax_error_text(st);
        printf("%s: %.*s\n", name, (int)why.len, (const char *)why.ptr);
    }
}

int main(void) {
    static unsigned char block[1 << 20];
    pinax_config config = {.struct_size = sizeof config};
    pinax_state *st;
    if (pinax_open((pinax_mem){block, sizeof block}, &config, &st) != PINAX_OK)
        return 1;
    pinax_set_resolver(st, resolve, NULL);
    try_load(st, "main",
             "extern gcd (a int, b int gives int) from \"mathlib\"\n"
             "extern count_vowels (s str gives int) from \"mathlib\" as vowels\n"
             "word answer (gives int) 84 36 gcd \"embeddable\" vowels add end\n");
    pinax_word w;
    int64_t out = 0;
    pinax_find(st, "answer", "(gives int)", 0, &w);
    pinax_run(st, w, &(pinax_args){.ri = &out});
    printf("gcd(84, 36) + vowels(\"embeddable\") = %lld\n", (long long)out);

    try_load(st, "other", "extern sqrt (x int gives int) from \"libm\"\n");   /* the host gives no such thing */
    pinax_close(st);
    return 0;
}

출력

$ ./18-extern
gcd(84, 36) + vowels("embeddable") = 16
other: extern: the host does not give this function

(5) 서명이 C 함수와 맞지 않으면 C 의 미정의 동작이다. 처리기는 그것을 알 수 없다. 믿지 못하는 스크립트를 돌리는 호스트는 해결 함수를 두지 않는다.

제약

(6) 이미 있는 이름(핵 낱말 등)의 함수는 as 로 다른 이름을 줘야 한다.

18-extern-as.pnx

extern abs (n int gives int) from "libc"

거절

$ pnx 18-extern-as.pnx
18-extern-as.pnx:1:8: this name is already defined: give the function another with `as`

(7) (3)이 허락하지 않는 서명은 거절된다.

18-extern-result.pnx

extern strlen (s cstr gives n int, m int) from "libc"

거절

$ pnx 18-extern-result.pnx
18-extern-result.pnx:1:8: extern: int, handle, str and cstr arguments (eight for C at most), one int or handle result; or fail only with a handle result
진단
조건메시지
as 없이 이미 있는 이름this name is already defined: give the function another with `as`
허락하지 않는 서명extern: int, handle, str and cstr arguments (eight for C at most), one int or handle result; or fail only with a handle result
호스트가 함수를 주지 않음extern: the host does not give this function