diff --git a/module/language/elisp/bindings.scm b/module/language/elisp/bindings.scm index 0d03ab521..4c8e15940 100644 --- a/module/language/elisp/bindings.scm +++ b/module/language/elisp/bindings.scm @@ -24,6 +24,7 @@ #:use-module (srfi srfi-9) #:use-module (srfi srfi-26) #:export (make-bindings + global? mark-global! map-globals with-lexical-bindings @@ -57,6 +58,9 @@ (define (make-bindings) (%make-bindings '() (make-hash-table))) +(define (global? bindings sym module) + (and=> (assoc-ref (globals bindings) module) (cut memq sym <>))) + ;;; Mark that a given symbol is needed as global in the specified ;;; slot-module. diff --git a/module/language/elisp/compile-tree-il.scm b/module/language/elisp/compile-tree-il.scm index 08cb0bdde..20d79421b 100644 --- a/module/language/elisp/compile-tree-il.scm +++ b/module/language/elisp/compile-tree-il.scm @@ -50,7 +50,8 @@ compile-defmacro compile-defun #{compile-`}# - compile-quote)) + compile-quote + compile-%set-lexical-binding-mode)) ;;; Certain common parameters (like the bindings data structure or ;;; compiler options) are not always passed around but accessed using @@ -66,6 +67,8 @@ (define always-lexical (make-fluid)) +(define lexical-binding (make-fluid)) + ;;; Find the source properties of some parsed expression if there are ;;; any associated with it. @@ -245,7 +248,10 @@ (let ((always (fluid-ref always-lexical))) (or (eq? always 'all) (memq sym always) - (get-lexical-binding (fluid-ref bindings-data) sym)))))) + (get-lexical-binding (fluid-ref bindings-data) sym) + (and + (fluid-ref lexical-binding) + (not (global? (fluid-ref bindings-data) sym module)))))))) (define (split-let-bindings bindings module) (let iterate ((tail bindings) @@ -854,6 +860,12 @@ ((,val) (make-const loc val)))) +(defspecial %set-lexical-binding-mode (loc args) + (pmatch args + ((,val) + (fluid-set! lexical-binding val) + (make-void loc)))) + ;;; Compile a compound expression to Tree-IL. (define (compile-pair loc expr) diff --git a/module/language/elisp/lexer.scm b/module/language/elisp/lexer.scm index af7e02add..2e52cc780 100644 --- a/module/language/elisp/lexer.scm +++ b/module/language/elisp/lexer.scm @@ -252,7 +252,15 @@ ;;; Main lexer routine, which is given a port and does look for the next ;;; token. +(define lexical-binding-regexp + (make-regexp + "-\\*-(|.*;)[ \t]*lexical-binding:[ \t]*([^;]*[^ \t;]).*-\\*-")) + (define (lex port) + (define (lexical-binding-value string) + (and=> (regexp-exec lexical-binding-regexp string) + (lambda (match) + (not (member (match:substring match 2) '("nil" "()")))))) (let ((return (let ((file (if (file-port? port) (port-filename port) #f)) @@ -283,11 +291,19 @@ (case c ;; A line comment, skip until end-of-line is found. ((#\;) - (let iterate () - (let ((cur (read-char port))) - (if (or (eof-object? cur) (char=? cur #\newline)) - (lex port) - (iterate))))) + (if (= (port-line port) 0) + (let iterate ((chars '())) + (let ((cur (read-char port))) + (if (or (eof-object? cur) (char=? cur #\newline)) + (let ((string (list->string (reverse chars)))) + (return 'set-lexical-binding-mode! + (lexical-binding-value string))) + (iterate (cons cur chars))))) + (let iterate () + (let ((cur (read-char port))) + (if (or (eof-object? cur) (char=? cur #\newline)) + (lex port) + (iterate)))))) ;; A character literal. ((#\?) (return 'character (get-character port #f))) diff --git a/module/language/elisp/parser.scm b/module/language/elisp/parser.scm index df825eb4e..e83f136bb 100644 --- a/module/language/elisp/parser.scm +++ b/module/language/elisp/parser.scm @@ -201,6 +201,8 @@ (setter expr) (force-promises! expr) expr)) + ((set-lexical-binding-mode!) + (return `(%set-lexical-binding-mode ,(cdr token)))) (else (parse-error token "expected expression, got" token))))) diff --git a/module/language/elisp/runtime/function-slot.scm b/module/language/elisp/runtime/function-slot.scm index 3fe47fb6e..edb4ebf63 100644 --- a/module/language/elisp/runtime/function-slot.scm +++ b/module/language/elisp/runtime/function-slot.scm @@ -41,7 +41,9 @@ (compile-defun . defun) (compile-defmacro . defmacro) (#{compile-`}# . #{`}#) - (compile-quote . quote))) + (compile-quote . quote) + (compile-%set-lexical-binding-mode + . %set-lexical-binding-mode))) #:duplicates (last) ;; special operators #:re-export (progn @@ -64,6 +66,7 @@ defun defmacro #{`}# - quote) + quote + %set-lexical-binding-mode) ;; functions #:re-export (apply)) diff --git a/test-suite/tests/elisp-compiler.test b/test-suite/tests/elisp-compiler.test index ebef0c243..819884fb8 100644 --- a/test-suite/tests/elisp-compiler.test +++ b/test-suite/tests/elisp-compiler.test @@ -295,7 +295,7 @@ (defun dyna () a) (lexical-let ((a 2) (b 42)) (and (= a 2) (= (dyna) 1) - ((lambda (a) (and (= a 3) (= b 42) (= (dyna) 3))) 3) + ((lambda (a) (and (= a 3) (= b 42) (= (dyna) 1))) 3) ((lambda () (let ((a 3)) (and (= a 3) (= (dyna) 1))))) (= a 2) (= (dyna) 1)))