From f4c44a3ba7f88e8171e1e2bd14b82d00b5912977 Mon Sep 17 00:00:00 2001 From: Phil Date: Sat, 7 May 2011 16:15:11 -0500 Subject: [PATCH] Add some documentation. Function calls now properly handle multiple values resulting from a function call as the last argument. doc/ref/api-languages.texi: Add a small blurb about Lua. module/language/lua/compile-tree-il.scm: Function calls now properly handle multiple values resulting from a function call as the last argument. --- doc/ref/api-languages.texi | 26 ++++++- module/language/lua/compile-tree-il.scm | 91 ++++++++++++++++++------- module/language/lua/notes.org | 19 +++++- module/language/lua/parser.scm | 3 +- module/language/lua/runtime.scm | 5 +- test-suite/tests/lua-eval-2.test | 5 +- test-suite/tests/lua-eval-3.test | 16 ++++- test-suite/tests/lua-eval.test | 3 - test-suite/tests/lua-lexer.test | 8 +-- 9 files changed, 130 insertions(+), 46 deletions(-) diff --git a/doc/ref/api-languages.texi b/doc/ref/api-languages.texi index fb42987d9..87d362951 100644 --- a/doc/ref/api-languages.texi +++ b/doc/ref/api-languages.texi @@ -9,7 +9,7 @@ In addition to Scheme, a user may write a Guile program in an increasing number of other languages. Currently supported languages include Emacs -Lisp and ECMAScript. +Lisp, ECMAScript, and Lua. Guile is still fundamentally a Scheme, but it tries to support a wide variety of language building-blocks, so that other languages can be @@ -24,6 +24,7 @@ Virtual Machine}.) * Using Other Languages:: How to use other languages. * Emacs Lisp:: The dialect of Lisp used in Emacs. * ECMAScript:: As seen on television. +* Lua:: A flexible scripting language. @end menu @@ -270,6 +271,29 @@ perhaps by some more responsible hacker. In the meantime, the charitable user might investigate such invocations as @code{,L ecmascript} and @code{cat test-suite/tests/ecmascript.test}. +@node Lua +@subsection Lua + +@url{http://www.lua.org,Lua} +is a powerful scripting language similar in many ways to Scheme. +Support for Lua 5.1 was added to Guile to make Guile a more attractive +option for extending programs, and to make it possible for projects +already using Lua to switch to Guile without giving up support for Lua. + +This section of the manual documents Guile's implementation of Lua. For +documentation of the Lua language, please see +@url{http://www.lua.org/docs.html,Lua's official documentation}. + +Guile's implementation of Lua is intended to behave exactly like the +reference implementation of Lua 5.1, but problems are to be expected. If +your Lua programs act strangely, it may very well be a problem with +Guile's Lua. + +Notes: + +collectgarbage is not fully supported. Attempting to use collectgarbage +with an argument other than ``collect'' will result in a warning being +printed. @c Local Variables: @c TeX-master: "guile.texi" diff --git a/module/language/lua/compile-tree-il.scm b/module/language/lua/compile-tree-il.scm index dcd2956cc..48c1a58b1 100644 --- a/module/language/lua/compile-tree-il.scm +++ b/module/language/lua/compile-tree-il.scm @@ -20,6 +20,7 @@ (define-module (language lua compile-tree-il) #:use-module (language tree-il) + #:use-module (srfi srfi-1) #:use-module (srfi srfi-39) #:use-module ((system base syntax) #:select (record-case)) #:use-module (rnrs control) @@ -35,20 +36,45 @@ (define (ref-runtime src name) + "Shorthand for referring to a variable in the (language lua runtime) module" (make-module-ref src *runtime-name* name #t)) (define (make-runtime-application src name arguments) - "Apply a function in the (language lua runtime) module" + "Shorthand for creating an application of a function in the (language lua runtime) module" (make-application src (ref-runtime src name) arguments)) (define (make-table-ref src table index) + "Shorthand for calling the index function in (language lua runtime)" (make-runtime-application src 'index (list table (if (symbol? index) (make-const src (symbol->string index)) index)))) (define (make-table-set! src table index exp) + "Shorthand for calling the new-index! function in (language lua runtime)" (make-runtime-application src 'new-index! (list table (if (symbol? index) (make-const src (symbol->string index)) index) exp))) +;; Calling conventions +(define* (make-plain-lambda-case src args gensyms body #:optional alternate) + (make-lambda-case src args #f #f #f '() (or gensyms args) body alternate)) + +(define* (make-plain-lambda src args gensyms body #:optional alternate) + (make-lambda src '() + (make-plain-lambda-case src args gensyms body alternate))) + +(define (make-arg-ignoring-lambda src body) + (make-lambda src '() + (make-lambda-case src '() #f '_ #f '() (list (gensym "_")) + body #f))) + +(define (make-catch-all-lambda src body rest-gensym) + (make-lambda src '() + (make-lambda-case src '() #f 'rest #f '() (list rest-gensym) + body #f))) + +(define (make-argless-lambda src body) + (make-plain-lambda src '() #f body)) + + ;; FIXME: use prompt and abort rather than catch and throw (define (apply-named-lua-function src name get-body) (let* ((name (gensym (string-append " " name))) @@ -81,24 +107,15 @@ (make-application src (make-lexical-ref src loop loop) '()))) (make-void src))))) -;; calling conventions -(define* (make-plain-lambda-case src args gensyms body #:optional alternate) - (make-lambda-case src args #f #f #f '() (or gensyms args) body alternate)) +(define (could-result-in-multiple-values? x) + (if (not (null? x)) + (let ((last-expr (last x))) + (or (ast-function-call? last-expr) (ast-variable-arguments? last-expr))) + #f)) -(define* (make-plain-lambda src args gensyms body #:optional alternate) - (make-lambda src '() - (make-plain-lambda-case src args gensyms body alternate))) - -(define (make-arg-ignoring-lambda src body) - (make-lambda src '() - (make-lambda-case src '() #f '_ #f '() (list (gensym "_")) - body #f))) - -(define (make-argless-lambda src body) - (make-plain-lambda src '() #f body)) - -(define (adjust-to-single-value src exp) - "adjust an expression so that it only returns one result; the rest are +;; TODO REMOVE +#;(define (adjust-to-single-value src exp) + "Adjust an expression so that it only returns one result; the rest are dropped silently" ;; Rely on the truncating behavior of returning multiple values to a ;; singly-valued continuation. @@ -153,12 +170,40 @@ dropped silently" #f)))) ((ast-function-call src operator operands) - #| (let* ((proc (compile operator)) - (args (make-application src (make-primitive-ref src 'list) (map-compile operands))) - (app-args (make-application src (make-primitive-ref src 'list) (list proc args))) - (app (make-application src (make-primitive-ref src 'apply) (list (make-primitive-ref src 'apply) app-args)))) |# (let* ((proc (compile operator)) - (app (make-application src proc (map-compile operands)))) + ;; will be #t if the the last expression in the list is a + ;; function call or variable arguments, which means we need + ;; to account for # + (need-to-apply-multiple-values? (could-result-in-multiple-values? operands)) + (args (map-compile operands))) + (define app + (if need-to-apply-multiple-values? + ;; Get the last function's (the one that could result in + ;; multiple values) return values using call-with-values + ;; and a function that takes variable arguments. Then + ;; append those variable arguments to the rest of the + ;; expression, and apply the first function to it) + (make-application src + (make-primitive-ref src 'call-with-values) + (list + (make-argless-lambda src (make-sequence src (last-pair args))) + (let ((rest-gensym (gensym "rest"))) + (make-catch-all-lambda src + (make-application src (make-primitive-ref src 'apply) + (list + proc + (make-application src + (make-module-ref src '(srfi srfi-1) 'append! #t) + (list + (make-application src (make-primitive-ref src 'list) (drop-right args 1)) + (make-lexical-ref src 'rest rest-gensym))))) + rest-gensym)))) + + (make-application src proc args))) + + ;; If this is function is a global variable, prepend a call to + ;; check-global-function to make sure it's defined before + ;; applying it (if (ast-global-ref? operator) (make-sequence src (list diff --git a/module/language/lua/notes.org b/module/language/lua/notes.org index 4e24cb95f..70f833bd5 100644 --- a/module/language/lua/notes.org +++ b/module/language/lua/notes.org @@ -5,17 +5,32 @@ This is an org-mode todo list of stuff that needs to be done for Guile Lua. CLOSED: [2011-04-19 Tue 19:36] ** DONE Standard library functions: math.modf, math.fmod CLOSED: [2011-04-21 Thu 15:43] -** TODO Variable arguments and multiple returns +** TODO Assignment cannot be naive left-to-right + a,b = b,a should work correctly +** STARTED Multiple values + What's the deal? In Lua, the rightmost expression in a + comma-delimited list (such as in a function application, or the + right-hand side of an assignment) may result in multiple values. + +** TODO Variable arguments + +** TODO Function environments (getfenv and setfenv) ** TODO Use prompt and abort instead of throw and catch ** TODO Standard library function: module ** TODO Standard library function: table.sort ** TODO Get the official Lua 5.1 test suite running +** TODO Lua working at the REPL +** TODO Document some stuff * Eh ** TODO Standard library function: math.frexp Pending some additions to Guile's numeric tower. +** TODO Use module binders ** TODO Better testing of standard library modules io, os -** TODO Function environments (getfenv and setfenv) +** TODO compile-tree-il.scm should be rewritten + Right now it's a jungle of tree-il constructors. There's a lot of + boilerplate code that could be made much nicer using macros and + parse-tree-il. * Differences Here are some difference in Guile Lua's behavior that should not cause diff --git a/module/language/lua/parser.scm b/module/language/lua/parser.scm index b602d20ab..dbe73f66f 100644 --- a/module/language/lua/parser.scm +++ b/module/language/lua/parser.scm @@ -351,7 +351,7 @@ (enforce-next! #\)) ;; finished save)) - (else (syntax-error (get-source-info) "unexpected symbol ~a" token)))) + (else (syntax-error (get-source-info) "unexpected token ~a" token)))) ;; index -> '[' expression ']' (define (index) @@ -598,7 +598,6 @@ ;; FIXME: does a left-to-right assignment, so x, y = y, x probably ;; doesn't work. Also does not appear to handle the x, y = foo() case. - ;; (define (parse-assignment src left right) ;; and then parses it, branching to handle overflows on either side if necessary (make-ast-sequence diff --git a/module/language/lua/runtime.scm b/module/language/lua/runtime.scm index 6a0541273..496f5122c 100644 --- a/module/language/lua/runtime.scm +++ b/module/language/lua/runtime.scm @@ -332,10 +332,7 @@ (runtime-error "~a" (if (null? opts) "assertion failed" (car opts))))) ;; NOTE: collectgarbage cannot be fully implemented because it expects -;; an incremental garbage collector that matches lua's interface; libgc -;; can be incremental but i don't think we can turn that on from guile -;; currently, and even if we could i'm not sure that libgc exposes what -;; lua wants +;; an incremental garbage collector that matches lua's interface (define-global collectgarbage (lambda* (opt #:optional (arg #nil)) (define (ignore) (runtime-warning "collectgarbage cannot respect command ~a" opt)) diff --git a/test-suite/tests/lua-eval-2.test b/test-suite/tests/lua-eval-2.test index 928cebe86..0787a3f94 100644 --- a/test-suite/tests/lua-eval-2.test +++ b/test-suite/tests/lua-eval-2.test @@ -18,9 +18,6 @@ (define-module (test-lua) #:use-module (ice-9 format) - #:use-module (language tree-il) - #:use-module (srfi srfi-1) - #:use-module (srfi srfi-8) #:use-module (system base compile) #:use-module (test-suite lib) @@ -102,7 +99,7 @@ (test "print \"hello world\"; return true") ;; variable arguments - #;(test "function test(...) print(...) end test(1,2)") + (test "function test(...) print(...) end test(1,2)") ;; numeric for loop (test "for x = 1,2,1 do print(true) end return true") diff --git a/test-suite/tests/lua-eval-3.test b/test-suite/tests/lua-eval-3.test index 533d4a85b..16a4935b3 100644 --- a/test-suite/tests/lua-eval-3.test +++ b/test-suite/tests/lua-eval-3.test @@ -18,9 +18,6 @@ (define-module (test-lua) #:use-module (ice-9 format) - #:use-module (language tree-il) - #:use-module (srfi srfi-1) - #:use-module (srfi srfi-8) #:use-module (system base compile) #:use-module (test-suite lib) @@ -41,9 +38,22 @@ ((_ string) (test string #t))))) +#| ;; make sure logical expressions don't evaluate expressions twice ;;; y will equal 2 in case of extra eval (test "y = 0 function tmp() y = y + 1 return true end assert(tmp() or tmp()) return y == 1") ;;; y will equal 4 in case of extra eval (test "y = 0 function void(x) end function tmp() y = y + 2 return false end; function tmp2() y = y + 1 return true end; void(tmp() and tmp2()) return y == 2") +|# + 1 )) + + +(define (from-string string) + (compile ((make-parser (open-input-string string))) + #:from 'lua + #:to 'value)) + +;(format #t "~a\n" (from-string "function tmp() return 4,5 end print(1,2,3,tmp())")) + +(format #t "~a\n" (from-string "function tmp(...) print(1,2,3,...) end tmp(4,5)")) diff --git a/test-suite/tests/lua-eval.test b/test-suite/tests/lua-eval.test index 7008314aa..96bff7285 100644 --- a/test-suite/tests/lua-eval.test +++ b/test-suite/tests/lua-eval.test @@ -18,9 +18,6 @@ (define-module (test-lua) #:use-module (ice-9 format) - #:use-module (language tree-il) - #:use-module (srfi srfi-1) - #:use-module (srfi srfi-8) #:use-module (system base compile) #:use-module (test-suite lib) diff --git a/test-suite/tests/lua-lexer.test b/test-suite/tests/lua-lexer.test index 77094a0df..51e9efabf 100644 --- a/test-suite/tests/lua-lexer.test +++ b/test-suite/tests/lua-lexer.test @@ -6,12 +6,12 @@ ;;;; 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 @@ -29,7 +29,7 @@ (call-with-input-string string make-lexer)) (lambda (get-source-info lex) (lex)))) - + (let-syntax ((test (syntax-rules (eof) @@ -62,7 +62,7 @@ comment]]")) (test "name" 'name) (test "return" #:return) (test ".." #:concat) - (test "..." #:dots) + (test "..." #:vararg) (test ";" #\;) (test "-" #\-) (test "+" #\+)