1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-01 20:30:28 +02:00
guile/module/language/lua/standard/io.scm
No Itisnt a30c18c22a add lua language implementation
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.
2013-09-09 17:01:23 +01:00

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))