1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-05-09 23:40:29 +02:00

lua/parser tweaks

* module/language/lua/parser.scm (define-record, define-ast): Simplify
  these macros.
  (make-parser): A number of small idiomatic changes.
This commit is contained in:
Andy Wingo 2010-12-10 11:41:39 +01:00 committed by Ian Price
parent a0cecd8ff2
commit 48f7c66a40

View file

@ -26,16 +26,13 @@
;; compiling the source. ;; compiling the source.
(define-module (language lua parser) (define-module (language lua parser)
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-8) #:use-module (srfi srfi-8)
#:use-module (srfi srfi-9) #:use-module (srfi srfi-9)
#:use-module (rnrs control) #:use-module (rnrs control)
#:use-module (language lua common) #:use-module (language lua common)
#:use-module (language lua lexer) #:use-module (language lua lexer)
#:use-module (language lua runtime) #:use-module (language lua runtime)
#:export (make-parser read-lua)) #:export (make-parser read-lua))
;; Implicitly named records ;; Implicitly named records
@ -43,16 +40,17 @@
((define-record ((define-record
(lambda (stx) (lambda (stx)
(define (id template-id . rest) (define (id template-id . rest)
(datum->syntax template-id (string->symbol (apply string-append (map (lambda (x) (if (string? x) x (symbol->string (syntax->datum x)))) rest))))) (datum->syntax template-id
(apply symbol-append (map syntax->datum rest))))
(syntax-case stx () (syntax-case stx ()
((_ name field ...) ((_ name field ...)
(with-syntax (with-syntax
((constructor (id #'name "make-" #'name)) ((constructor (id #'name 'make- #'name))
(predicate (id #'name #'name "?")) (predicate (id #'name #'name '?))
((setter ...) ((setter ...)
(map (lambda (x) (id #'name #'name "-" x "!")) #'(field ...))) (map (lambda (x) (id #'name #'name '- x '!)) #'(field ...)))
((getter ...) ((getter ...)
(map (lambda (x) (id #'name #'name "-" x)) #'(field ...)))) (map (lambda (x) (id #'name #'name '- x)) #'(field ...))))
#'(begin #'(begin
(define-record-type name (define-record-type name
@ -71,11 +69,12 @@
(define-ast (define-ast
(lambda (stx) (lambda (stx)
(define (id template-id . rest) (define (id template-id . rest)
(datum->syntax template-id (string->symbol (apply string-append (map (lambda (x) (if (string? x) x (symbol->string (syntax->datum x)))) rest))))) (datum->syntax template-id
(apply symbol-append (map syntax->datum rest))))
(syntax-case stx () (syntax-case stx ()
((_ "aux" name field ...) ((_ "aux" name field ...)
#`(define-record #,(id #'name "ast-" #'name) #,(id #'name "src") field ...)) #`(define-record #,(id #'name 'ast- #'name) #,(id #'name 'src) field ...))
((_ (name field ...) ...) ((_ (name field ...) ...)
#`(begin #`(begin
@ -123,17 +122,19 @@
"Returns true if TOKEN denotes the end of a grammatical chunk." "Returns true if TOKEN denotes the end of a grammatical chunk."
(or (memq token '(#:else #:elseif #:end #:until)) (eof-object? token))) (or (memq token '(#:else #:elseif #:end #:until)) (eof-object? token)))
(define *special-tokens*
'(#\. #\( #\) #\, #\- #\< #\; #\+ #\{ #\} #\[ #\] #\: #\#
#:function #:end #:if #:return #:elseif #:then #:else #:true #:false
#:nil #:== #:~= #:= #\> #:>= #:<= #:local #:dots #:break #:do #:in))
(define (token/type t) (define (token/type t)
(cond ((number? t) 'NUMBER) (cond
((number? t) 'NUMBER)
((eof-object? t) 'EOS) ((eof-object? t) 'EOS)
((symbol? t) 'NAME) ((symbol? t) 'NAME)
((string? t) 'STRING) ((string? t) 'STRING)
(else ((memv t *special-tokens*) t)
(case t (else (error 'TOKEN/TYPE t))))
((#\. #\( #\) #\, #\- #\< #\; #\+ #\{ #\} #\[ #\] #\: #\#
#:function #:end #:if #:return #:elseif #:then #:else #:true #:false
#:nil #:== #:~= #:= #\> #:>= #:<= #:local #:dots #:break #:do #:in) t)
(else (error 'TOKEN/TYPE t))))))
;; infix operator parsing ;; infix operator parsing
(define (binary-operator? t) (define (binary-operator? t)
@ -161,22 +162,37 @@
(define (make-lua-assignment left right) (define (make-lua-assignment left right)
"Generate an assignment from a variable and an expression" "Generate an assignment from a variable and an expression"
(cond ((ast-global-ref? left) (cond
(make-ast-global-set (ast-global-ref-src left) (ast-global-ref-name left) right)) ((ast-global-ref? left)
(make-ast-global-set (ast-global-ref-src left)
(ast-global-ref-name left)
right))
((ast-local-ref? left) ((ast-local-ref? left)
(make-ast-local-set (ast-local-ref-src left) (ast-local-ref-name left) (ast-local-ref-gensym left) right)) (make-ast-local-set (ast-local-ref-src left)
(ast-local-ref-name left)
(ast-local-ref-gensym left)
right))
((ast-table-ref? left) ((ast-table-ref? left)
(make-ast-table-set (ast-table-ref-src left) (ast-table-ref-table left) (ast-table-ref-key left) right)) (make-ast-table-set (ast-table-ref-src left)
(ast-table-ref-table left)
(ast-table-ref-key left)
right))
(else (else
(error 'MAKE-LUA-ASSIGNMENT "should not happen")))) (error 'MAKE-LUA-ASSIGNMENT "should not happen"))))
(define (wrap-expression-in-environment src e x) (define (wrap-expression-in-environment src e x)
"Wrap an expression in an enclosing lexical environment if necessary" "Wrap an expression in an enclosing lexical environment if necessary"
(let* ((bindings (map cdr (environment-bindings e))) (let ((locals (filter-map (lambda (binding)
(locals (filter-map (lambda (b) (if (eq? (binding-type b) 'local) b #f)) bindings))) (let ((b (cdr binding)))
(and (eq? (binding-type b) 'local) b)))
(environment-bindings e))))
(if (null? locals) (if (null? locals)
x x
(make-ast-local-block src (map binding-name locals) (map binding-gensym locals) (map (lambda (c) *nil-literal*) locals) x)))) (make-ast-local-block src
(map binding-name locals)
(map binding-gensym locals)
(map (lambda (c) *nil-literal*) locals)
x))))
;;;;; PARSER ;;;;; PARSER
@ -216,7 +232,9 @@
(define (environment-define! name type) (define (environment-define! name type)
"Define a new variable with NAME and TYPE" "Define a new variable with NAME and TYPE"
(if (not (member name (environment-bindings environment))) (if (not (member name (environment-bindings environment)))
(environment-bindings! environment (alist-cons name (environment-bindings!
environment
(acons name
(make-binding (make-binding
name name
(gensym (string-append " " (symbol->string name))) (gensym (string-append " " (symbol->string name)))
@ -227,35 +245,24 @@
;; because Lua allows global variables to be referenced without being ;; because Lua allows global variables to be referenced without being
;; predefined ;; predefined
(define (environment-lookup-aux name . e) (define* (environment-lookup-aux name #:optional (e environment))
"Given a variable's NAME, look up its binding." "Given a variable's NAME, look up its binding."
(set! e (if (null? e) environment (car e ))) (and e (or (assq-ref (environment-bindings e) name)
(if e (environment-lookup-aux name (environment-parent e)))))
(let ((binding (assq-ref (environment-bindings e) name)))
(if binding
binding
(environment-lookup-aux name (environment-parent e))))
#f))
(define (environment-lookup-gensym name) (define (environment-lookup-gensym name)
"Given a variable's NAME, look up its gensym" "Given a variable's NAME, look up its gensym"
(define binding (environment-lookup-aux name)) (and=> (environment-lookup-aux name) binding-gensym))
(if binding
(binding-gensym binding)
#f))
(define (environment-lookup-type name) (define (environment-lookup-type name)
"Given a variable's NAME, look up its global" "Given a variable's NAME, look up its global"
(define binding (environment-lookup-aux name)) (and=> (environment-lookup-aux name) binding-type))
(if binding
(binding-type binding)
#f))
(define (resolve-ref src name) (define (resolve-ref src name)
"Determine whether a variable reference is global or local" "Determine whether a variable reference is global or local"
(let* ((binding (environment-lookup-gensym name))) (let ((sym (environment-lookup-gensym name)))
(if binding (if sym
(make-ast-local-ref src name binding) (make-ast-local-ref src name sym)
(make-ast-global-ref src name)))) (make-ast-global-ref src name))))
;;;;; LEXER INTERACTION ;;;;; LEXER INTERACTION
@ -270,7 +277,7 @@
(define-syntax advance! (define-syntax advance!
(syntax-rules () (syntax-rules ()
((_ x) (begin (advance-aux) x)) ((_ x) (let ((t x)) (advance-aux) t))
((_) (advance-aux)))) ((_) (advance-aux))))
(define (assert-token-type type) (define (assert-token-type type)
@ -280,9 +287,7 @@
(define (maybe-skip-next! c) (define (maybe-skip-next! c)
"Skip a token" "Skip a token"
(if (equal? token c) (and (equal? token c) (advance! #t)))
(advance! #t)
#f))
(define (enforce-next! expect) (define (enforce-next! expect)
"Throw an error if the current token is not the expected token" "Throw an error if the current token is not the expected token"
@ -292,34 +297,32 @@
;;;;; GRAMMAR ;;;;; GRAMMAR
;; single-name -> NAME ;; single-name -> NAME
(define (single-name . return-src?) (define (single-name)
(define save token) (let ((save token))
(define src (get-source-info))
(assert-token-type 'NAME) (assert-token-type 'NAME)
(advance!) (advance!)
(if (not (null? return-src?))
(values src save)
save)) save))
;; single-variable -> single-name ;; single-variable -> single-name
(define (single-variable) (define (single-variable)
(receive (src save) (let* ((src (get-source-info))
(single-name #:return-src #t) (save (single-name)))
(resolve-ref src save))) (resolve-ref src save)))
;; application-arguments -> '(' [ expression-list ] ')' | STRING | TABLE ;; application-arguments -> '(' [ expression-list ] ')' | STRING | TABLE
(define (application-arguments) (define (application-arguments)
(cond ((eq? (token/type token) 'STRING) (case (token/type token)
(let* ((string token)) ((STRING)
(let ((string token))
(advance!) (advance!)
(list (make-ast-literal #f string)))) (list (make-ast-literal #f string))))
((eq? token #\{) ((#\{)
;; TODO: table constructor ;; TODO: table constructor
;; '(' ;; '('
(list (table-literal))) (list (table-literal)))
((eq? token #\() ((#\()
(advance!) (advance!)
(if (eq? token #\)) (if (eqv? token #\))
;; ')' ;; ')'
(advance! '()) (advance! '())
;; [ expression-list ] ;; [ expression-list ]
@ -327,75 +330,73 @@
;; ')' ;; ')'
(enforce-next! #\)) (enforce-next! #\))
arguments))) arguments)))
(else (error 'APPLICATION-ARGUMENTS "should not happen")))) (else
(error 'APPLICATION-ARGUMENTS "should not happen"))))
;; prefix-expression -> NAME | '(' expression ')' ;; prefix-expression -> NAME | '(' expression ')'
(define (prefix-expression) (define (prefix-expression)
(cond (case (token/type token)
;; NAME ;; NAME
((eq? (token/type token) 'NAME) (single-variable)) ((NAME) (single-variable))
;; '(' ;; '('
((eq? token #\() ((#\()
(begin
(advance!) (advance!)
;; expression ;; expression
(let* ((save (expression))) (let ((save (expression)))
;; ')' ;; ')'
(enforce-next! #\)) (enforce-next! #\))
;; finished ;; finished
save))) save))
(else (syntax-error (get-source-info) "unexpected symbol ~a" token)))) (else (syntax-error (get-source-info) "unexpected symbol ~a" token))))
;; index -> '[' expression ']' ;; index -> '[' expression ']'
(define (index) (define (index)
(enforce-next! #\[) (enforce-next! #\[)
(let* ((indice (expression))) (let ((index (expression)))
(enforce-next! #\]) (enforce-next! #\])
indice)) index))
;; field-selector -> '.' NAME ;; field-selector -> '.' NAME
(define (field-selector src prefix) (define (field-selector src prefix)
(make-ast-table-ref src prefix (make-ast-literal src (symbol->string (single-name))))) (make-ast-table-ref src prefix
(make-ast-literal src (symbol->string (single-name)))))
;; primary-expression -> prefix-expression { field-selector [ application-arguments ] | index | application-arguments } ;; primary-expression -> prefix-expression { field-selector [ application-arguments ] | index | application-arguments }
(define (primary-expression) (define (primary-expression)
(define src (get-source-info)) (let ((src (get-source-info)))
;; prefix-expression ;; prefix-expression
(define prefix (prefix-expression)) (let lp ((expr (prefix-expression)))
(let lp ((expr prefix))
(case (token/type token) (case (token/type token)
;; field-selector ;; field-selector
((#\.) (advance!) (lp (field-selector src expr))) ((#\.) (advance!) (lp (field-selector src expr)))
;; index ;; index
((#\[) ((#\[) (lp (make-ast-table-ref src expr (index))))
(let* ((indice (index)))
(lp (make-ast-table-ref src expr indice))))
;; ':' NAME application-arguments ;; ':' NAME application-arguments
((#\:) ((#\:)
(advance!) (advance!)
(assert-token-type 'NAME) ;; FIXME: double-evaluation of expr
(let* ((name (single-name))) (let* ((name (single-name))
(lp (args (application-arguments)))
(make-ast-function-call src (lp (make-ast-function-call
(make-ast-table-ref src expr (make-ast-literal src (symbol->string name))) src
(cons expr (application-arguments)))))) (make-ast-table-ref
src expr (make-ast-literal src (symbol->string name)))
(cons expr args)))))
;; application-arguments ;; application-arguments
((#\( STRING #\{) ((#\( STRING #\{)
(lp (make-ast-function-call src expr (application-arguments)))) (lp (make-ast-function-call src expr (application-arguments))))
(else expr)))) (else expr)))))
;; expression-statement -> function | assignment ;; expression-statement -> function | assignment
(define (expression-statement) (define (expression-statement)
(define primary (primary-expression)) (let ((primary (primary-expression)))
(if (ast-function-call? primary) (if (ast-function-call? primary)
primary primary
(assignment primary))) (assignment primary))))
;; record-field -> (NAME | index) '=' expression ;; record-field -> (NAME | index) '=' expression
(define (record-field) (define (record-field)
(let* ((indice (let* ((index (cond
(cond
;; NAME ;; NAME
((eq? (token/type token) 'NAME) ((eq? (token/type token) 'NAME)
(let ((tmp (make-ast-literal #f (symbol->string token)))) (let ((tmp (make-ast-literal #f (symbol->string token))))
@ -403,13 +404,12 @@
tmp)) tmp))
;; index ;; index
(else (index)))) (else (index))))
(value (value (begin
(begin
;; '=' ;; '='
(enforce-next! #:=) (enforce-next! #:=)
;; expression ;; expression
(expression)))) (expression))))
(values indice value))) (values index value)))
;; field -> expression | record-field ;; field -> expression | record-field
(define (field) (define (field)
@ -425,63 +425,59 @@
;; field-separator -> ',' | ';' ;; field-separator -> ',' | ';'
;; table-fields -> [ field { field-separator field } [ field-separator ] ] ;; table-fields -> [ field { field-separator field } [ field-separator ] ]
(define (table-fields src) (define (table-fields src)
(if (eq? token #\}) (let loop ((implicit-index 1)
'()
(let loop ((implicit-indice 1)
(tree '())) (tree '()))
(if (eq? token #\}) (if (eqv? token #\})
(reverse! tree) (reverse! tree)
(receive (receive (index expr) (field)
(indice expr)
(field)
;; field-separator ;; field-separator
(maybe-skip-next! #\,) (maybe-skip-next! #\,)
(maybe-skip-next! #\;) (maybe-skip-next! #\;)
(loop (loop
(if (not indice) (+ implicit-indice 1) implicit-indice) (if (not index) (+ implicit-index 1) implicit-index)
(cons (cons
(cons (or indice (make-ast-literal src implicit-indice)) expr) (cons (or index (make-ast-literal src implicit-index)) expr)
tree))))))) tree))))))
;; table-literal -> '{' table-fields '}' ;; table-literal -> '{' table-fields '}'
(define (table-literal) (define (table-literal)
(define src (get-source-info)) (let ((src (get-source-info)))
;; '{' ;; '{'
(enforce-next! #\{) (enforce-next! #\{)
;; bind the table to a temporary variable with LET as it's needed in order to initialize the table ;; bind the table to a temporary variable with LET as it's needed
(let* ((result (make-ast-table-literal src (table-fields src)))) ;; in order to initialize the table
result (let ((result (make-ast-table-literal src (table-fields src))))
(enforce-next! #\}) (enforce-next! #\})
result)) result)))
;; parameter-list -> [ parameter { ',' parameter } ] ;; parameter-list -> [ parameter { ',' parameter } ]
(define (parameter-list function-name) (define (parameter-list function-name)
(if (eq? token #\))
(values '() #f)
(let lp ((parameters '())) (let lp ((parameters '()))
;; parameter (case (token/type token)
(let* ((parameters ((NAME)
(if (eq? (token/type token) 'NAME) (let ((parameters (cons token parameters)))
(append! parameters (list token))
(if (eq? token #:dots)
(values parameters #f)
(syntax-error (get-source-info) "expected either a name or ... in the parameter list of '~a', but got ~a" function-name token))))
(last-token token))
(advance!) (advance!)
(if (eq? token #\,) (if (maybe-skip-next! #\,)
(if (eq? last-token #:dots) (lp parameters)
(syntax-error (get-source-info) "expected ')' after ... in the parameter list of '~a'" function-name) (values (reverse! parameters) #f))))
(advance! (lp parameters))) ((#\))
(values parameters (eq? last-token #:dots))))))) (values (reverse! parameters) #f))
((#:dots)
(advance!)
(values (reverse! parameters) #t))
(else
(syntax-error
(get-source-info)
"expected either a name or ... in the parameter list of '~a', but got ~a"
function-name token)))))
;; function-body -> '(' parameter-list ')' chunk END ;; function-body -> '(' parameter-list ')' chunk END
(define* (function-body #:optional (src (get-source-info)) (implicit-self? #f) (name 'anonymous)) (define* (function-body #:optional (src (get-source-info)) (implicit-self? #f)
(name 'anonymous))
;; '(' ;; '('
(enforce-next! #\() (enforce-next! #\()
;; parameter-list ;; parameter-list
(receive (parameters variable-arguments?) (receive (parameters variable-arguments?) (parameter-list name)
(parameter-list name)
(let* ((old-vararg-function *vararg-function*)) (let* ((old-vararg-function *vararg-function*))
(set! *vararg-function* variable-arguments?) (set! *vararg-function* variable-arguments?)
(enforce-next! #\)) (enforce-next! #\))
@ -494,10 +490,17 @@
(let* ((body (chunk)) (let* ((body (chunk))
(parameter-gensyms (map environment-lookup-gensym parameters)) (parameter-gensyms (map environment-lookup-gensym parameters))
(result (result
(make-ast-function src (if (eq? name 'anonymous) #f name) (make-ast-function
(if implicit-self? (append parameters '(self)) parameters) src (if (eq? name 'anonymous) #f name)
(if implicit-self? (append parameter-gensyms (list (environment-lookup-gensym 'self))) parameter-gensyms) (if implicit-self?
variable-arguments? (if (null? body) *void-literal* body)))) (append parameters '(self))
parameters)
(if implicit-self?
(append parameter-gensyms
(list (environment-lookup-gensym 'self)))
parameter-gensyms)
variable-arguments?
(if (null? body) *void-literal* body))))
(leave-environment!) (leave-environment!)
;; END ;; END
(enforce-next! #:end) (enforce-next! #:end)
@ -515,60 +518,48 @@
;; simple-expression -> (nil | true | false | NUMBER | STRING) | table-literal | FUNCTION function-body ;; simple-expression -> (nil | true | false | NUMBER | STRING) | table-literal | FUNCTION function-body
(define (simple-expression) (define (simple-expression)
(define src (get-source-info)) (let ((src (get-source-info)))
(receive
(advance? result)
(case (token/type token) (case (token/type token)
;; (nil | true | false | NUMBER | STRING) ;; (nil | true | false | NUMBER | STRING)
((#:true #:false #:nil NUMBER STRING) ((#:nil) (advance! (make-ast-literal src #nil)))
(values ((#:true) (advance! (make-ast-literal src #t)))
#t ((#:false) (advance! (make-ast-literal src #f)))
(make-ast-literal ((NUMBER STRING) (advance! (make-ast-literal src token)))
src
(cond ((eq? token #:true) #t)
((eq? token #:false) #f)
((eq? token #:nil) #nil)
(else token)))))
;; table-literal ;; table-literal
((#\{) (values #f (table-literal))) ((#\{) (table-literal))
;; ... ;; ...
((#:dots) ((#:dots)
(unless *vararg-function* (unless *vararg-function*
(syntax-error src "cannot use '...' outside of a variable arguments function")) (syntax-error src "cannot use '...' outside of a variable arguments function"))
(values #t (make-ast-variable-arguments src))) (advance! (make-ast-variable-arguments src)))
;; FUNCTION function-body ;; FUNCTION function-body
((#:function) (advance!) (values #f (function-body src))) ((#:function) (advance!) (function-body src))
;; primary-expression ;; primary-expression
(else (values #f (primary-expression)))) (else (primary-expression)))))
(if advance?
(advance!))
result))
;; subexpression -> (simple-expression | unary-operator subexpression) { binary-operator subexpression } ;; subexpression -> (simple-expression | unary-operator subexpression) { binary-operator subexpression }
(define (subexpression limit) (define (subexpression limit)
(define left) (let loop ((left
;; test for preceding unary operator
(set! left
;; (simple-expression | unary-operator subexpression) ;; (simple-expression | unary-operator subexpression)
(if (unary-operator? token) (if (unary-operator? token)
;; unary-operator subexpression ;; unary-operator subexpression
(let* ((src (get-source-info)) (let* ((src (get-source-info))
(operator token)) (operator token))
(advance!) (advance!)
(make-ast-unary-operation src operator (subexpression *unary-priority*))) (make-ast-unary-operation
src operator (subexpression *unary-priority*)))
;; simple-expression ;; simple-expression
;; note: simple-expression may advance the current token ;; note: simple-expression may advance the current token
(simple-expression))) (simple-expression))))
(let loop ((left left))
;; { binary-operator subexpression } ;; { binary-operator subexpression }
(if (and (binary-operator? token) (> (priority token) limit)) (if (and (binary-operator? token) (> (priority token) limit))
(let* ((src (get-source-info)) (let* ((src (get-source-info))
(operator token)) (operator token))
(advance!) (advance!)
(loop (make-ast-binary-operation
src operator left
;; read next expression with higher priorities ;; read next expression with higher priorities
(let* ((right (subexpression (priority operator)))) (subexpression (priority operator)))))
(loop (make-ast-binary-operation src operator left right))))
;; finished ;; finished
left))) left)))
@ -578,30 +569,32 @@
;; while-statement -> WHILE expression DO chunk END ;; while-statement -> WHILE expression DO chunk END
(define (while-statement) (define (while-statement)
(define src (get-source-info)) (let ((src (get-source-info)))
;; WHILE ;; WHILE
(advance!) (advance!)
;; expression ;; expression
(let* ((condition (expression))) (let ((condition (expression)))
;; DO ;; DO
(enforce-next! #:do) (enforce-next! #:do)
;; chunk ;; chunk
(let* ((body (chunk))) (let ((body (chunk)))
;; END ;; END
(enforce-next! #:end) (enforce-next! #:end)
(make-ast-while-loop src condition body)))) (make-ast-while-loop src condition body)))))
;; return-statement -> RETURN expression-list ;; return-statement -> RETURN expression-list
(define (return-statement) (define (return-statement)
(define src (get-source-info)) (let ((src (get-source-info)))
;; RETURN ;; RETURN
(advance!) (advance!)
(make-ast-return src
(make-ast-return src (if (or (end-of-chunk? token) (eq? token #\;)) (if (or (end-of-chunk? token) (eqv? token #\;))
*void-literal* *void-literal*
(expression-list)))) (expression-list)))))
;; 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) (define (parse-assignment src left right)
;; and then parses it, branching to handle overflows on either side if necessary ;; and then parses it, branching to handle overflows on either side if necessary
(make-ast-sequence (make-ast-sequence
@ -640,20 +633,17 @@
;; so this function accumulates the entire assignment ;; so this function accumulates the entire assignment
(let* ((src (get-source-info)) (let* ((src (get-source-info))
(left (let loop ((x first) (left (let loop ((tree (list first)))
(tree '())) (if (eqv? token #\,)
(set! tree (cons x tree)) (begin
(if (eq? token #\,) (advance!)
(advance! (loop (primary-expression) tree)) (loop (cons (primary-expression) tree)))
(reverse! tree)))) (reverse! tree))))
(right (begin (right (begin
(enforce-next! #:=) (enforce-next! #:=)
(expression-list)))) (expression-list))))
(parse-assignment src left right) (parse-assignment src left right)))
) ; let*
) ; assignment
;; then-chunk -> (IF | ELSEIF) expression THEN chunk ;; then-chunk -> (IF | ELSEIF) expression THEN chunk
(define (then-chunk) (define (then-chunk)
@ -664,82 +654,78 @@
;; THEN ;; THEN
(enforce-next! #:then) (enforce-next! #:then)
;; chunk ;; chunk
(let* ((body (chunk))) (values condition (chunk))))
(values condition body))))
;; if-statement -> then-chunk { then-chunk } [ELSE chunk] END ;; if-statement -> then-chunk { then-chunk } [ELSE chunk] END
(define (if-statement) (define (if-statement)
(define if-src (get-source-info)) (let ((src (get-source-info)))
(define x (receive (test then) (then-chunk)
(receive (test then) (let ((x (make-ast-condition
(then-chunk) src test then
(make-ast-condition
if-src test then
(let lp () (let lp ()
(define src (get-source-info)) (let ((src (get-source-info)))
(if (eq? token #:elseif) (if (eq? token #:elseif)
(receive (test then) (receive (test then) (then-chunk)
(then-chunk)
(make-ast-condition src test then (lp))) (make-ast-condition src test then (lp)))
(if (eq? token #:else) (if (eq? token #:else)
(advance! (chunk)) (begin (advance!) (chunk))
*void-literal*)))))) *void-literal*)))))))
(enforce-next! #:end) (enforce-next! #:end)
x) x))))
;; repeat-statement -> REPEAT chunk UNTIL expression ;; repeat-statement -> REPEAT chunk UNTIL expression
(define (repeat-statement) (define (repeat-statement)
(define src (get-source-info)) (let ((src (get-source-info)))
;; REPEAT ;; REPEAT
(advance!) (advance!)
;; chunk ;; chunk
(let* ((body (chunk))) (let ((body (chunk)))
;; UNTIL ;; UNTIL
(enforce-next! #:until) (enforce-next! #:until)
;; expression ;; expression
(let* ((condition (expression))) (let ((condition (expression)))
(make-ast-while-loop (make-ast-while-loop
src src
(make-ast-unary-operation src 'not condition) (make-ast-unary-operation src 'not condition)
body)))) body)))))
;; function-statement -> FUNCTION NAME { field-selector } [ ':' NAME ] function-body ;; function-statement -> FUNCTION NAME { field-selector } [ ':' NAME ] function-body
(define (function-statement) (define (function-statement)
(define src (get-source-info)) (let* ((src (get-source-info))
;; FUNCTION NAME ;; FUNCTION NAME
(define name (advance! (single-name))) (name (begin (advance!) (single-name))))
(receive (prefix type) (receive (prefix type)
(let lp ((last-expr (resolve-ref src name))) (let lp ((last-expr (resolve-ref src name)))
(if (eq? token #\.) (if (eqv? token #\.)
;; { '.' NAME } ;; { '.' NAME }
(let* ((name (advance! (single-name)))) (let ((name (begin (advance!) (single-name))))
(if (eq? token #\() (if (eq? token #\()
(values (cons name last-expr) 'table-function) (values (cons name last-expr) 'table-function)
(lp (make-ast-table-ref src last-expr name)))) (lp (make-ast-table-ref src last-expr name))))
;; [ ':' NAME ] ;; [ ':' NAME ]
(if (eq? token #\:) (if (eqv? token #\:)
(let* ((name (advance! (single-name)))) (let ((name (begin (advance!) (single-name))))
(values (cons name last-expr) 'table-method)) (values (cons name last-expr) 'table-method))
(values last-expr 'function)))) (values last-expr 'function))))
(define body (function-body src (eq? type 'table-method) name)) (let ((body (function-body src (eq? type 'table-method) name)))
(case type (case type
((table-function table-method) ((table-function table-method)
(make-ast-table-set src (cdr prefix) (make-ast-literal src (symbol->string (car prefix))) body)) (make-ast-table-set
((function) (make-lua-assignment prefix body)) src (cdr prefix)
(else (error 'FUNCTION-STATEMENT "should not happen"))))) (make-ast-literal src (symbol->string (car prefix))) body))
((function)
(make-lua-assignment prefix body))
(else (error 'FUNCTION-STATEMENT "should not happen")))))))
;; local-statement -> LOCAL NAME { ',' NAME } [ '=' expression-list ] ;; local-statement -> LOCAL NAME { ',' NAME } [ '=' expression-list ]
(define (local-statement) (define (local-statement)
(define src (get-source-info)) (let ((src (get-source-info)))
;; LOCAL ;; LOCAL
;; (already advanced by calling function) ;; (already advanced by calling function)
(let lp ((names '())) (let lp ((names '()))
;; NAME ;; NAME
(assert-token-type 'NAME) (assert-token-type 'NAME)
(set! names (cons token names)) (let ((names (advance! (cons token names))))
(advance!)
(if (maybe-skip-next! #\,) (if (maybe-skip-next! #\,)
;; { ',' NAME } ;; { ',' NAME }
(lp names) (lp names)
@ -747,123 +733,104 @@
(for-each (lambda (n) (environment-define! n 'local)) names) (for-each (lambda (n) (environment-define! n 'local)) names)
(if (maybe-skip-next! #:=) (if (maybe-skip-next! #:=)
;; [ '=' expression-list ] ;; [ '=' expression-list ]
(let* ((left (map (lambda (x) (resolve-ref src x)) names)) (let ((left (map (lambda (x) (resolve-ref src x))
(right (expression-list))) (reverse! names))))
(parse-assignment src left (reverse! right))) (parse-assignment src left (expression-list)))
;; otherwise, it's not a declaration, not an assignment, and evaluates to nothing ;; otherwise, it's not a declaration, not an
*void-literal*))))) ;; assignment, and evaluates to nothing
*void-literal*)))))))
(define (local-function-statement) (define (local-function-statement)
(assert-token-type 'NAME) (assert-token-type 'NAME)
(let* ((name token)) (let ((name token))
(environment-define! name 'local) (environment-define! name 'local)
(advance!) (advance!)
(make-ast-local-set (get-source-info) name (environment-lookup-gensym name) (function-body)))) (make-ast-local-set (get-source-info) name
(environment-lookup-gensym name)
(function-body))))
;; for-body ;; for-body
(define (for-body) (define (for-body)
(enforce-next! #:do) (enforce-next! #:do)
(let* ((body (chunk))) (let ((body (chunk)))
(enforce-next! #:end) (enforce-next! #:end)
body)) body))
;; numeric-for -> FOR NAME '=' expression ',' expression ',' expression DO chunk END ;; numeric-for -> FOR NAME '=' expression ',' expression ',' expression DO chunk END
(define (numeric-for src name) (define (numeric-for src name)
(define step *default-for-step*)
(advance!)
(enforce-next! #:=) (enforce-next! #:=)
(enter-environment!) (enter-environment!)
(environment-define! name 'local) (environment-define! name 'local)
(let* ((initial (expression))) (let ((initial (expression)))
(enforce-next! #\,) (enforce-next! #\,)
(let* ((limit (expression))) (let* ((limit (expression))
(when (eq? token #\,) (step (if (maybe-skip-next! #\,)
(advance!) (expression)
(set! step (expression))) *default-for-step*))
(let* ((result (make-ast-numeric-for-loop src name initial limit step (for-body)))) (result (make-ast-numeric-for-loop src name initial limit step
(for-body))))
(leave-environment!) (leave-environment!)
result)))) result)))
;; list-for -> FOR NAME { ',' NAME } IN expression-list DO chunk END ;; list-for -> FOR NAME { ',' NAME } IN expression-list DO chunk END
(define (list-for src name) (define (list-for src name)
(let* ((names (let ((names (let lp ((names (list name)))
(let lp ((names (list name))) (if (maybe-skip-next! #\,)
(advance!) (lp (cons (single-name) names))
(if (eq? token #\,)
(begin
(advance!)
(assert-token-type 'NAME)
(lp (cons token names)))
(reverse! names))))) (reverse! names)))))
(enforce-next! #:in) (enforce-next! #:in)
(let* ((exps (expression-list))) (let ((exps (expression-list)))
(enforce-next! #:do) (enforce-next! #:do)
(for-each (for-each
(lambda (name) (lambda (name)
(environment-define! name 'hidden)) (environment-define! name 'hidden))
names) names)
(let* ((body (chunk))) (let ((body (chunk)))
(enforce-next! #:end) (enforce-next! #:end)
(make-ast-list-for-loop src names (map environment-lookup-gensym names) exps body))))) (make-ast-list-for-loop
src names (map environment-lookup-gensym names) exps body)))))
;; for-statement -> FOR (numeric-for | list-for) END ;; for-statement -> FOR (numeric-for | list-for) END
(define (for-statement) (define (for-statement)
(define src (get-source-info)) (let ((src get-source-info))
(enforce-next! #:for) (enforce-next! #:for)
(assert-token-type 'NAME) (let ((name (single-name)))
(let* ((name token) (if (eq? token #:=)
(result
(begin
(lookahead!)
(if (eq? token2 #:=)
(numeric-for src name) (numeric-for src name)
(if (memv token2 '(#:in #\,)) (if (memv token '(#:in #\,))
(list-for src name) (list-for src name)
(syntax-error src "expected = or in after for variable")))))) (syntax-error src "expected = or in after for variable"))))))
result))
;; break-statement -> BREAK ;; break-statement -> BREAK
(define (break-statement) (define (break-statement)
(let ((src (get-source-info)))
(enforce-next! #:break) (enforce-next! #:break)
(make-ast-break (get-source-info))) (make-ast-break src)))
;; statement ;; statement
(define (statement) (define (statement)
(case token (case token
((#\;) (advance!) (statement)) ((#\;) (advance!) (statement))
;; statement -> return ;; statement -> return
((#:return #:break) ((#:return) (values #t (return-statement)))
(values ((#:break) (values #t (break-statement)))
#t ((#:repeat) (values #f (repeat-statement)))
(case token ((#:while) (values #f (while-statement)))
((#:return) (return-statement)) ((#:if) (values #f (if-statement)))
((#:break) (break-statement))))) ((#:function) (values #f (function-statement)))
((#:if #:function #:do #:while #:repeat #:local #:for) ((#:local) (advance!) (if (maybe-skip-next! #:function)
(values (values #f (local-function-statement))
#f (values #f (local-statement))))
(case token ((#:for) (values #f (for-statement)))
((#:repeat) (repeat-statement)) ((#:do) (advance!) (let ((body (chunk)))
((#:while) (while-statement))
((#:if) (if-statement))
((#:function) (function-statement))
((#:local)
(advance!)
(if (maybe-skip-next! #:function)
(local-function-statement)
(local-statement)))
((#:for) (for-statement))
((#:do)
(begin
(advance!)
(let* ((body (chunk)))
(enforce-next! #:end) (enforce-next! #:end)
body)))))) (values #f body)))
;; statement -> function | assignment ;; statement -> function | assignment
(else (values #f (expression-statement))))) (else (values #f (expression-statement)))))
;; chunk -> { statement [ ';' ] } ;; chunk -> { statement [ ';' ] }
(define (chunk) (define (chunk)
(define src (get-source-info)) (let ((src (get-source-info)))
(let loop ((is-last (end-of-chunk? token)) (let loop ((is-last (end-of-chunk? token))
(tree '())) (tree '()))
(if is-last (if is-last
@ -873,10 +840,8 @@
src src
environment environment
(make-ast-sequence src (reverse! tree)))) (make-ast-sequence src (reverse! tree))))
(receive (receive (is-last node) (statement)
(is-last node) (loop (or (end-of-chunk? token) is-last) (cons node tree)))))))
(statement)
(loop (or (end-of-chunk? token) is-last) (cons node tree))))))
(initialize-lua-lexer! port get-source-info lexer) (initialize-lua-lexer! port get-source-info lexer)
@ -888,5 +853,4 @@
chunk) chunk)
(define (read-lua port) (define (read-lua port)
(define parser (make-parser port)) ((make-parser port)))
(parser))