mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-01 20:30:28 +02:00
What is missing: + Functions: module, getfenv, setfenv, math.modf, table.sort + Parser: needs to be more flexible + Compiler: needs more extensive work to properly handle all possible cases of variable arguments, multiple returns, and loops + Language: Variable arguments and unpacking of multiple returns. (For example we need to be able to handle something as complex as print(unpack({...})), which is easy with Lua's explicit stack but will require lots of tree-il gymnastics, or perhaps modifications to better allow different calling conventions. (For instance -- how would we support Python or Ruby, where keyword arguments are gathered into a hashtable and passed as a single argument?) What is there: A fair shot at supporting Lua 5.1, not quite a drop-in replacement, but not far from that goal either.
185 lines
6 KiB
Scheme
185 lines
6 KiB
Scheme
;;; Guile Lua --- io standard library
|
|
|
|
;;; Copyright (C) 2010 Free Software Foundation, Inc.
|
|
;;;
|
|
;;; This library is free software; you can redistribute it and/or
|
|
;;; modify it under the terms of the GNU Lesser General Public
|
|
;;; License as published by the Free Software Foundation; either
|
|
;;; version 3 of the License, or (at your option) any later version.
|
|
;;;
|
|
;;; This library is distributed in the hope that it will be useful,
|
|
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
;;; Lesser General Public License for more details.
|
|
;;;
|
|
;;; You should have received a copy of the GNU Lesser General Public
|
|
;;; License along with this library; if not, write to the Free Software
|
|
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
;;; Code:
|
|
|
|
(define-module (language lua standard io)
|
|
#:use-module (language lua runtime)
|
|
|
|
#:use-module (ice-9 popen)
|
|
#:use-module (ice-9 rdelim)
|
|
#:use-module (rnrs control))
|
|
|
|
;; io.file:read
|
|
|
|
;; metatable for file objects
|
|
(define file (make-table))
|
|
|
|
(rawset file '__index
|
|
(lambda (self key)
|
|
(rawget file key)))
|
|
|
|
(define stdin (current-input-port))
|
|
(define stdout (current-output-port))
|
|
(define stderr (current-error-port))
|
|
|
|
(define* (close #:optional (file stdout))
|
|
(close-port file))
|
|
|
|
(rawset file 'close
|
|
(lambda (self)
|
|
(close self)))
|
|
|
|
;; lua doesn't actually have an optional flush argument, but this is more in line with everything else
|
|
(define* (flush #:optional (file stdout))
|
|
(force-output file))
|
|
|
|
(rawset file 'flush
|
|
(lambda (self)
|
|
(flush self)))
|
|
|
|
(define* (input #:optional filename)
|
|
(if filename
|
|
(let* ((file (open filename)))
|
|
(set! stdin file)
|
|
file)
|
|
stdin))
|
|
|
|
(define (line-iterator file auto-close?)
|
|
(lambda ()
|
|
(let* ((line (read-line file)))
|
|
(if (eof-object? line)
|
|
(begin
|
|
(if auto-close?
|
|
(close-port file))
|
|
#nil)
|
|
line))))
|
|
|
|
(define* (lines #:optional filename)
|
|
(let* ((file (if filename (open filename) stdin)))
|
|
(line-iterator file (and filename))))
|
|
|
|
(rawset file 'lines
|
|
(lambda (self)
|
|
(line-iterator self #f)))
|
|
|
|
(define* (open filename #:optional (mode "r"))
|
|
(assert-string 1 "io.open" filename)
|
|
(assert-string 2 "io.open" mode)
|
|
(let* ((handle (open-file filename mode)))
|
|
(register-userdata! handle file)
|
|
handle))
|
|
|
|
(define* (output #:optional filename)
|
|
(if filename
|
|
(let* ((file (open filename "w")))
|
|
(set! stdout file)
|
|
file)
|
|
stdout))
|
|
|
|
(define* (popen prog #:optional (mode "r"))
|
|
(assert-string 2 "io.popen" mode)
|
|
(open-pipe
|
|
prog
|
|
(if (string=? mode "w") OPEN_WRITE OPEN_READ)))
|
|
|
|
(define (default-read port)
|
|
(if (eof-object? (peek-char port))
|
|
#nil
|
|
(read-line port)))
|
|
|
|
(rawset file 'read
|
|
(lambda (self . formats)
|
|
(if (null? formats)
|
|
(default-read self)
|
|
(apply
|
|
values
|
|
(map
|
|
(lambda (self . formats)
|
|
(unless (or (number? format) (string? format))
|
|
(runtime-error "'file:read' expects a string or number as format argument, but got ~a" format))
|
|
(if (number? format)
|
|
(if (eof-object? (peek-char self))
|
|
#nil
|
|
(let lp ((out (open-output-string))
|
|
(i format))
|
|
(if (= i 0)
|
|
(get-output-string out)
|
|
(let ((c (read-char self)))
|
|
(if (eof-object? self)
|
|
(get-output-string out)
|
|
(begin
|
|
(write-char c out)
|
|
(lp out (- i 1))))))))
|
|
|
|
(let* ((format-length (string-length format))
|
|
(c1 (if (> format-length 0) (string-ref format 0) #f))
|
|
(c2 (if (> format-length 1) (string-ref format 1) #f)))
|
|
(cond ((eq? c2 #\n) (runtime-error "'file:read' number reading is not yet supported"))
|
|
((eq? c2 #\a)
|
|
(if (eof-object? (peek-char self))
|
|
#nil
|
|
(let lp ((out (open-output-string)))
|
|
(let ((c (read-char self)))
|
|
(if (eof-object? c)
|
|
(get-output-string out)
|
|
(begin
|
|
(write-char c out)
|
|
(lp out)))))))
|
|
((eq? c2 #\l)
|
|
(default-read self))
|
|
(else
|
|
(runtime-error "file:read does not understand format ~a" format))))))
|
|
formats)))))
|
|
|
|
(rawset file 'seek
|
|
(lambda* (self #:optional (whence "cur") (offset 0))
|
|
(assert-string 1 "file:seek" whence)
|
|
(assert-number 2 "file:seek" offset)
|
|
(seek self offset
|
|
(cond ((string=? whence "cur") SEEK_CUR)
|
|
((string=? whence "set") SEEK_SET)
|
|
((string=? whence "end") SEEK_END)
|
|
(else (runtime-error "invalid 'whence' argument to 'file:seek'; expected \"cur\", \"set\", or \"end\""))))))
|
|
|
|
(rawset file 'setvbuf
|
|
(lambda* (self mode #:optional size)
|
|
(assert-string 1 "file:setvbuf" mode)
|
|
(let* ((translated-mode
|
|
(cond ((string=? mode "no") _IONBF)
|
|
((string=? mode "line") _IOLBF)
|
|
((string=? mode "full") _IOFBF))))
|
|
(if size
|
|
(setvbuf self mode)
|
|
(setvbuf self mode size)))))
|
|
|
|
(rawset file 'write
|
|
(lambda* (self . args)
|
|
(for-each
|
|
(lambda (arg)
|
|
(unless (or (string? arg) (number? arg))
|
|
(runtime-error "'file:write' expects string or number as argument but got '~a'" arg))
|
|
(display arg self))
|
|
args)))
|
|
|
|
(define (type obj)
|
|
(if (port? obj)
|
|
(if (port-closed? obj)
|
|
"closed"
|
|
"file")
|
|
#nil))
|