(error "Can't have a prim that wants a token and has a precedence of less than 20: ~A" prim))
(let*
((args (if (prim-is-infix prim) (cdr (prim-args prim)) (prim-args prim)))
- (half-parsed-remainder (parse-internal (cdr lexed-ast) :remaining-args (append args (list :done-with-args))))
+ (half-parsed-remainder
+ (parse-internal (cdr lexed-ast) :remaining-args (append args (list :done-with-args))))
(breakpoint (or
(position-if (lambda (form) (or (not (listp form)) (not (eql :arg (car form))))) half-parsed-remainder)
(length half-parsed-remainder)))
(already-parsed-limbo-forms
(subseq half-parsed-remainder breakpoint (min (length args) (length half-parsed-remainder))))
+ (num-optional-forms (- (length args) breakpoint))
(middle-forms
(cons
(if
(prim-is-infix prim)
- (reconfigure-due-to-precedence prev-item prim (mapcar #'cadr (subseq half-parsed-remainder 0 breakpoint)))
+ ; There's a potential bug here about infix operators with optional forms, where the first item is optional
+ ; I don't consider that super likely though...
+ (append
+ (reconfigure-due-to-precedence prev-item prim (mapcar #'cadr (subseq half-parsed-remainder 0 breakpoint)))
+ (loop :repeat num-optional-forms :collect :optional))
(cons
(prim-name prim)
- (mapcar #'cadr (subseq half-parsed-remainder 0 breakpoint))))
- already-parsed-limbo-forms)))
+ (append
+ (mapcar #'cadr (subseq half-parsed-remainder 0 breakpoint))
+ (loop :repeat num-optional-forms :collect :optional)))) ; we save the place for optionals for the transpiler
+ already-parsed-limbo-forms)));)
+ (let
+ ((arg-at-bp (nth breakpoint args)))
+ (when (and arg-at-bp (or (not (listp arg-at-bp)) (not (find :optional arg-at-bp))))
+ (error "Stopped collecting arguments, but non optional arguments remain")))
(append
(butlast middle-forms)
(parse-internal
Calling eval on that code should work correctly as long as you have a
running engine."
-
(let
((*dynamic-prims*
(mapcar
(append (list :func (getf prim :macro)) prim)
(append (list :func (lambda (&rest args) `(funcall ,(getf prim :func) ,@args))) prim)))
dynamic-prims)))
- (cond
- ((command-list-p parsed-ast) (transpile-commands parsed-ast))
- ((and (listp parsed-ast) (= 1 (length parsed-ast)) (reporter-p (car parsed-ast)))
- (transpile-reporter (car parsed-ast)))
- (t (error "Is neither a list of commands nor a reporter: ~S" parsed-ast)))))
+ (let
+ ((deoptionalized-ast (deoptionalize parsed-ast)))
+ (cond
+ ((command-list-p deoptionalized-ast) (transpile-commands deoptionalized-ast))
+ ((and (listp deoptionalized-ast) (= 1 (length deoptionalized-ast)) (reporter-p (car deoptionalized-ast)))
+ (transpile-reporter (car deoptionalized-ast)))
+ (t (error "Is neither a list of commands nor a reporter: ~S" deoptionalized-ast))))))
(defun command-list-p (parsed-ast)
"COMMAND-LIST-P PARSED-AST => RESULT
COMMAND-LIST-P returns whether the parsed-ast is a valid list
of commands."
+ (every #'command-p parsed-ast))
+
+(defun command-p (parsed-ast)
(and
- (every #'listp parsed-ast)
- (every #'prim-command-p (mapcar #'find-prim (mapcar #'car parsed-ast)))))
+ (listp parsed-ast)
+ (prim-command-p (find-prim (car parsed-ast)))))
(defun reporter-p (parsed-ast)
"REPORTER-P PARSED-AST => RESULT
REPORTER-P returns whether the parsed-ast is a valid reporter."
(and
+ (listp parsed-ast)
(symbolp (car parsed-ast))
(prim-reporter-p (find-prim (car parsed-ast)))))
`(progn
,@(transpile-commands-inner parsed-ast)))
+; This makes some assumptions about the precedence of optionality
+; Consider the following:
+; - command-1 <optional> <optional>
+; - reporter-1 <optional>
+;
+; And consider:
+; - command-1 reporter-1 "foo"
+;
+; Does the "foo" belong to command-1 or reporter-1?
+;
+; I'm not sure how netlogo answers this at this time, but for the purposes
+; of this deoptionalizer, "foo" belongs to reporter-1. In the case that the
+; language tests treat this as incorrect, I'll fix it. I'm not sure if this
+; case exists in the base netlogo language, and I don't feel like creating
+; a specific test case at this time to find out.
+(defun deoptionalize (parsed-ast)
+ (let
+ ((first-thing (car parsed-ast)))
+ (cond
+ ((not parsed-ast) nil)
+ ((not (listp first-thing)) (cons first-thing (deoptionalize (cdr parsed-ast))))
+ ((and
+ (find :optional first-thing)
+ (cadr parsed-ast)
+ (not (command-p (cadr parsed-ast))))
+ (let
+ ((deoptionalized-rest (deoptionalize (cdr parsed-ast)))
+ (optional-pos (position :optional first-thing)))
+ (deoptionalize
+ (cons
+ (append
+ (subseq first-thing 0 optional-pos)
+ (list (car deoptionalized-rest))
+ (subseq first-thing (1+ optional-pos)))
+ (cdr deoptionalized-rest)))))
+ ((find :optional first-thing)
+ (deoptionalize
+ (cons
+ (subseq first-thing 0 (position :optional first-thing))
+ (cdr parsed-ast))))
+ (t
+ (cons
+ (deoptionalize first-thing)
+ (deoptionalize (cdr parsed-ast)))))))
+
(defun transpile-commands-inner (parsed-ast)
(cond
((not parsed-ast) nil)