// This module contains a further set of utility functions, which are // to a greater or lesser extent generated. Both this file and the // file generated from it include Ünicöde characters, which should be // encoded in UTF-8. // // This file is part of Beastie // SPDX-FileCopyrightText: 2023 Norman Gray // SPDX-License-Identifier: BSD-2-Clause #include #include //#include // debugging only #include "beastie.h" #include "util-extra.h" #if 0 // remove this, when I remove the lex/yacc-generated subtex parser typedef struct { const char* cs; const int cslen; const codepoint_t arg; const codepoint_t cp; const char* str; } csarg_value_t; // Compare two pointers to csarg_value_t, for bsearch. // Note: strncmp is specified to work for null-terminated strings, // which means we can't use it to compare two strings of unequal length. static int compare_csarg_element(const void* e1_ptr, const void* e2_ptr) { const csarg_value_t* e1 = (const csarg_value_t*)e1_ptr; const csarg_value_t* e2 = (const csarg_value_t*)e2_ptr; int res; if (e1->cslen == e2->cslen) { res = strncmp(e1->cs, e2->cs, e1->cslen); } else { size_t shorter = (e1->cslen < e2->cslen ? e1->cslen : e2->cslen); res = strncmp(e1->cs, e2->cs, shorter); if (res == 0) res = (e1->cslen < e2->cslen ? -1 : +1); } if (res == 0) { res = e1->arg - e2->arg; } //fprintf(stderr, "compare '%.*s'+%d < '%.*s'+%d => %d\n", e1->cslen, e1->cs, e1->arg, e2->cslen, e2->cs, e2->arg, res); return res; } static csarg_value_t* lookup_accented_char_base(const char* s, int slen, codepoint_t arg) { #(let* ((char-file "misc/unicode/characters.scm") ;; this file is a list of ;; (codepoint char-as-string description "cs" [arg as char or int] ...) ;; where the last two parameters can be repeated ;; to indicate alternate cs+arg pairs (char-specs (with-input-from-file char-file read)) #;(char-specs (map (lambda (spec) `(,(car spec) ,(cadr spec) . ,(cdddr spec))) (with-input-from-file char-file read)))) ;(dependency-add! char-file) (let ((chars (sort! (let loop ((ss char-specs) (res '())) ;; turn ((codepoint char-as-string ignored "cs" ...) ...) ;; into ("cs" arg/int codepoint char-as-string)... (if (null? ss) res (let ((cp (caar ss)) (ch (cadar ss)) (cslist (cdddar ss))) (case (length cslist) ((1) (loop (cdr ss) (cons (list (car cslist) 0 cp ch) res))) ((2) (let ((cs (car cslist)) (arg (cadr cslist))) (loop (cdr ss) (cons (list cs (if (char? arg) (char->integer arg) arg) cp ch) res)))) (else (let ((cs (car cslist)) (arg (cadr cslist))) (loop `((,cp ,ch #f . ,(cddr cslist)) . ,(cdr ss)) (cons (list cs (if (char? arg) (char->integer arg) arg) cp ch) res)))))))) (lambda (a b) ;; sort by "cs", then arg (if (string=? (car a) (car b)) (< (cadr a) (cadr b)) (string %s\n", slen, s, arg, // (result == NULL ? "<>" : result->str)); return result; } codepoint_t lookup_accented_char_as_cp(const char* s, int slen, codepoint_t arg) { csarg_value_t* res = lookup_accented_char_base(s, slen, arg); return (res == NULL ? 0 : res->cp); } const char* lookup_accented_char_as_string(const char* s, int slen, codepoint_t arg) { csarg_value_t* res = lookup_accented_char_base(s, slen, arg); return (res == NULL ? NULL : res->str); } #endif // end of C-based command-lookup. // (discarded, since I don't _think_ I'll need this again, but I'm not positive) void load_runtime(void) { #(file->strings "config.scm") s7_load_c_string(S7, config_scm, config_scm_len); #(file->strings "runtime.scm") s7_load_c_string(S7, runtime_scm, runtime_scm_len); #include "readermacros.inc" s7_load_c_string(S7, readermacros_scm, readermacros_scm_len); #(let ((char-specs (with-input-from-file "misc/unicode/characters.scm" read))) (dependency-add! "misc/unicode/characters.scm") ;; this is a list of, eg, ;; (#xa3 "£" "POUND SIGN" "pounds") ;; or ;; (#xc0 "À" "LATIN CAPITAL LETTER A WITH GRAVE" "`" #\A) ;; where the contents after the description indicate either ;; (i) a single TeX command, such as "\pounds" or a command argument ;; pair such as (here) "\`{A}", and there can be multiple such ;; pairs. ;; ;; Turn these into a definition of %subtex-mappings% which ;; is a vector of ;; (string? . integer?) or (string? . (vector-of (integer? . integer?))). ;; The first is a mapping (eg) "\pounds" -> 163, ;; the second is "`" -> #(... (#x41 . #xc0)...) (let* ((charmap (sort! (map (lambda (cs) (let ((codepoint (car cs)) (char-as-string (cadr cs)) (commands (cdddr cs))) (if (= (length commands) 1) (list (car commands) codepoint) (apply values (let loop ((cmds commands)) (cond ((null? cmds) '()) ((null? (cdr cmds)) (error "unexpected end of list ~s" commands)) (else `((,(car cmds) ,(cadr cmds) ,codepoint) . ,(loop (cddr cmds)))))))))) char-specs) (lambda (a b) (stringinteger c) (list->vector (sort! (map (lambda (p) (let ((ci (car p))) (cons (if (char? ci) (char->integer ci) ci) (cdr p)))) l) (lambda (a b) (< (car a) (car b)))))) (let* ((prog (let loop ((c charmap) (result '()) (sublist-key #f) (sublist-value '())) (if (null? c) (if sublist-key (loop '() (cons (cons sublist-key (make-subkey-vector sublist-value)) result) #f #f) (reverse result)) (let ((c1 (car c))) (cond ((= (length c1) 2) (if sublist-key (loop c (cons (cons sublist-key (make-subkey-vector sublist-value)) result) #f #f) (loop (cdr c) `((,(car c1) . ,(cadr c1)) . ,result) #f #f))) (sublist-key (if (string=? (car c1) sublist-key) (loop (cdr c) result sublist-key `((,(cadr c1) . ,(caddr c1)) . ,sublist-value)) (loop c (cons (cons sublist-key (make-subkey-vector sublist-value)) result) #f #f))) (else (loop (cdr c) result (car c1) `((,(cadr c1) . ,(caddr c1)))))))))) (progstr (let-temporarily (((*s7* 'print-length) (*s7* 'most-positive-fixnum))) (call-with-output-string (lambda (out) (write `(define %subtex-mappings% ,(list->vector prog)) out)))))) (prog->strings (list progstr) "subtex_mappings" #t)))) // fprintf(stderr, "subtex_mappings[%lu] = <%.40s...%d>\n", subtex_mappings_len, subtex_mappings, // subtex_mappings[subtex_mappings_len]); s7_load_c_string(S7, subtex_mappings, subtex_mappings_len); } const char* get_module_content(const char* module_name, size_t* string_length) { struct module_list_s { const char* name; const char* content; size_t content_length; }; #(let ((files '((authors . "authors.scm") (aux . "parse-aux.scm") (bibtex . "bibtex.scm") (parse-bib2* . "parse-bib2.scm") (bst . "bst.scm") (markdown . "parse-markdown.scm") (utils . "scheme-utils.scm") (xexpr . "xexpr.scm") (klipspringer . "klipspringer.scm") (klipspringer/structs ;; As with 'klipspringer, but _including_ the struct procedures, ;; which are not otherwise provided from klipspringer. ;; This isn't currently documented/exposed: ;; the time to do that would be when overhauling ;; the doc/klipspringer.md documentation. (let ((expose '(make-empty empty? empty-result make-consumed consumed? consumed-result make-ok ok? ok-value ok-input make-error error? error-descriptions error-inputs make-parser-input input-car input-cdr)) (*module-name* "internal-for-klipspringer-structs")) (module/expose 'klipspringer) ;; now add the exposed procedures to the local *provides* let, ;; and define *provides* in the outer let to be this extended one (varlet (outlet (curlet)) '*provides* (apply varlet (cons *provides* (apply append (map (lambda (s) (list s (eval s))) expose))))))) (mdblock . "parse-mdblock.scm") (json . "parse-json.scm") (subtex . "parse-subtex.scm") (subtex* ;; Within this module, we define a separate *provides*-like let. ;; This is similar to 'subtex, but adds management procedures ;; which should only by used by beastie code (let () (module/expose 'subtex) (varlet (outlet (curlet)) '*provides* *provide-in-starred-version*))) (unicode . "unicode.scm")))) (let ((modules (map (lambda (spec) (let ((content (cdr spec)) (ident (car spec)) (name (format #f "~a_module" (list->string (map (lambda (c) ;; very simple canonicalisation to C symbol (if (or (char-alphabetic? c) (char-numeric? c)) c #\_)) (string->list (symbol->string (car spec)))))))) (if (list? content) (cons (cons (format #f "~%~%/* inline code for ~a */" ident) (prog->strings (let-temporarily (((*s7* 'print-length) (*s7* 'most-positive-fixnum))) (map (lambda (line) (call-with-output-string (lambda (out) (write line out)))) content)) name :static? #t :comment-length 100)) (format #f " { \"~a\", ~a, ~a_len }, /* inline */~%" ident name name)) (cons (file->strings content name :static? #t) (format #f " { \"~a\", ~a, ~a_len }, /* ~a */~%" ident name name content))))) files))) (apply append (map car modules) (list (format #f "~%~% static struct module_list_s modules[] = {~%")) (map cdr modules) (list (format #f " };~% static size_t modules_len = ~a;~%" (length files)))))) for (int i=0; i