[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Guile-commits] 01/02: multiple languages support via file extension or
From: |
Arne Babenhauserheide |
Subject: |
[Guile-commits] 01/02: multiple languages support via file extension or #lang header |
Date: |
Thu, 23 Feb 2023 15:42:45 -0500 (EST) |
arnebab pushed a commit to branch wip-load-lang
in repository guile.
commit a12ca2b999fa746334c626603990475a08937cdc
Author: Matt Wette <mwette@alumni.caltech.edu>
AuthorDate: Sun Feb 13 14:43:29 2022 -0800
multiple languages support via file extension or #lang header
From scripts/compile pushed default assumption of #:from as 'scheme down
into system/base/compile where filename and first line can be used to
deduce intended "from" language. If first line of a file is of the form
#lang ecmascript
then the file is assumed consist of source language "ecmascript".
* module/scripts/compile.scm (compile): changed default #:from to #f
from 'scheme
* module/system/base/compile.scm(lang-from-port, %file-extension-map,
add-lang-extension, lang-extension-for): added global
%file-extension-map with accessor lang-extension-for and updater
add-lang-extension. Also, added lang-from-port to parse first line,
looking for #lang.
* test-suite/tests.scm: added "load-lang" test.
* test-suite/Makefile.am(SCM_TESTS): added tests/load-lang.test
---
module/scripts/compile.scm | 2 +-
module/system/base/compile.scm | 74 ++++++++++++++++++++++++++++++++++++++---
test-suite/Makefile.am | 1 +
test-suite/tests/load-lang.test | 37 +++++++++++++++++++++
4 files changed, 108 insertions(+), 6 deletions(-)
diff --git a/module/scripts/compile.scm b/module/scripts/compile.scm
index 9bb1211f8..e71964085 100644
--- a/module/scripts/compile.scm
+++ b/module/scripts/compile.scm
@@ -209,7 +209,7 @@ There is NO WARRANTY, to the extent permitted by law.~%"))
(('optimizations . opts) opts)
(_ '())))
options)))
- (from (or (assoc-ref options 'from) 'scheme))
+ (from (assoc-ref options 'from))
(to (or (assoc-ref options 'to) 'bytecode))
(target (or (assoc-ref options 'target) %host-type))
(input-files (assoc-ref options 'input-files))
diff --git a/module/system/base/compile.scm b/module/system/base/compile.scm
index a33d012bd..3838060df 100644
--- a/module/system/base/compile.scm
+++ b/module/system/base/compile.scm
@@ -31,7 +31,9 @@
compile
decompile
default-warning-level
- default-optimization-level))
+ default-optimization-level
+ add-lang-extension
+ lang-extension-for))
(define (level-validator x)
@@ -44,6 +46,61 @@
(define default-warning-level (make-parameter 1 level-validator))
(define default-optimization-level (make-parameter 2 level-validator))
+
+(define (lang-from-port port)
+
+ (define (release chl)
+ (let loop ((chl chl))
+ (unless (null? chl)
+ (unread-char (car chl) port)
+ (loop (cdr chl))))
+ #f)
+
+ (define (return chl)
+ (string->symbol (reverse-list->string chl)))
+
+ (let loop ((cl '()) (st 0) (kl '(#\# #\l #\a #\n #\g)) (ch (read-char port)))
+ (case st
+ ((0) (cond ; read `#lang'
+ ((eof-object? ch) (release cl))
+ ((null? kl) (loop cl 1 kl ch))
+ ((char=? ch (car kl))
+ (loop (cons ch cl) st (cdr kl) (read-char port)))
+ (else (release (cons ch cl)))))
+ ((1) (cond ; skip spaces
+ ((eof-object? ch) (release cl))
+ ((char=? ch #\space) (loop (cons ch cl) st kl (read-char port)))
+ (else (loop cl 2 '() ch))))
+ ((2) (cond ; collect lang name
+ ((eof-object? ch) (return kl))
+ ((char=? ch #\newline) (return kl))
+ ((char-whitespace? ch) (loop cl 3 kl ch))
+ (else (loop cl st (cons ch kl) (read-char port)))))
+ ((3) (cond
+ ((eof-object? ch) (return kl))
+ ((char=? ch #\newline) (return kl))
+ (else (loop cl st kl (read-char port))))))))
+
+(define %file-extension-map
+ (make-parameter
+ '(("scm" . scheme)
+ ("el" . elisp)
+ ("js" . ecmascript))))
+
+(define (add-lang-extension tag lang)
+ (unless (and (string? tag) (symbol? lang))
+ (error "expecting string symbol"))
+ (%file-extension-map (acons tag lang %file-extension-map)))
+
+(define (lang-extension-for tag)
+ (assoc-ref (%file-extension-map) tag))
+
+(define* (lang-from-file file)
+ (let* ((ix (string-rindex file #\.))
+ (ext (and ix (substring file (1+ ix)))))
+ (and ext (assoc-ref (%file-extension-map) ext))))
+
+
;;;
;;; Compiler
;;;
@@ -81,7 +138,9 @@
(define (ensure-language x)
(if (language? x)
x
- (lookup-language x)))
+ (if x
+ (lookup-language x)
+ (lookup-language 'scheme))))
;; Throws an exception if `dir' is not writable. The mkdir occurs
;; before the check, so that we avoid races (possibly due to parallel
@@ -166,9 +225,9 @@
(define* (compile-file file #:key
(output-file #f)
- (from (current-language))
+ (from #f)
(to 'bytecode)
- (env (default-environment from))
+ (env #f)
(optimization-level (default-optimization-level))
(warning-level (default-warning-level))
(opts '())
@@ -179,7 +238,12 @@
(error "failed to create path for auto-compiled file"
file)))
(in (open-input-file file))
- (enc (file-encoding in)))
+ (enc (file-encoding in))
+ (from (or from
+ (lang-from-port in)
+ (lang-from-file file)
+ (current-language)))
+ (env (or env (default-environment from))))
;; Choose the input encoding deterministically.
(set-port-encoding! in (or enc "UTF-8"))
diff --git a/test-suite/Makefile.am b/test-suite/Makefile.am
index 16fa2e952..0842db640 100644
--- a/test-suite/Makefile.am
+++ b/test-suite/Makefile.am
@@ -75,6 +75,7 @@ SCM_TESTS = tests/00-initial-env.test \
tests/keywords.test \
tests/list.test \
tests/load.test \
+ tests/load-lang.test \
tests/match.test \
tests/match.test.upstream \
tests/modules.test \
diff --git a/test-suite/tests/load-lang.test b/test-suite/tests/load-lang.test
new file mode 100644
index 000000000..067a39ab0
--- /dev/null
+++ b/test-suite/tests/load-lang.test
@@ -0,0 +1,37 @@
+;;;; load-lang.test - test loading extension languages -*- scheme -*-
+;;;;
+
+
+(define-module (test-suite test-load-lang)
+ #:use-module (test-suite lib)
+ #:declarative? #f)
+
+(define tmp-dir (getcwd))
+
+(define (data-file-name filename)
+ (in-vicinity tmp-dir filename))
+
+(with-test-prefix "load-lang"
+
+ (pass-if "using #lang"
+ (let ((src-file (data-file-name "load1js")))
+ (with-output-to-file src-file
+ (lambda ()
+ (display "#lang ecmascript\n")
+ (display "function js_1pl(b) { return 1 + b; }\n")))
+ (load src-file)
+ ;;(delete-file src-file)
+ (= (js_1pl 2) 3)))
+
+ #;(pass-if "using dot-js"
+ (let ((src-file (data-file-name "load2.js")))
+ (with-output-to-file src-file
+ (lambda ()
+ (display "function js_2pl(b) { return 2 + b; }\n")))
+ (load src-file)
+ ;;(delete-file src-file)
+ (= (js_2pl 2) 4)))
+
+ )
+
+;; --- last line ---