Prims - Implement round
[clnl] / src / main / parse.lisp
index eaaa3fe1d5fdf70c82d43c77431caacfaf5e7dea..957d177b54792e5a833f371a581b2a7d2c4ca26c 100644 (file)
 (defparameter *dynamic-prims* nil)
 
 (defun prim-name (prim) (getf prim :name))
-(defun prim-num-args (prim) (length (getf prim :args)))
 (defun prim-args (prim) (getf prim :args))
 (defun prim-structure-prim (prim) (getf prim :structure-prim))
 (defun prim-is-infix (prim) (getf prim :infix))
 
-(defun find-prim (symb) (find symb *prims* :key #'prim-name))
+(defun find-prim (symb)
+ (or
+  (find symb *prims* :key #'prim-name)
+  (find symb *dynamic-prims* :key #'prim-name)))
 
 ; Make this only as complicated as it needs to be, letting it grow
 ; as we take on more and more of the language
  "PARSE LEXED-AST &optional DYNAMIC-PRIMS => AST
 
   DYNAMIC-PRIMS: DYNAMIC-PRIM*
+  DYNAMIC-PRIM: (:name NAME :args ARGS :infix INFIX)
+  ARGS: ARG*
 
 ARGUMENTS AND VALUES:
 
   LEXED-AST: An ambigious ast
   AST: An unambigious ast that can be transpiled
-  DYNAMIC-PRIM: A prim not statically defined
+  NAME: A symbol in the keyword package
+  INFIX: Boolean denoting whether the prim is infix
+  ARG: A list of symbols denoting the type of argument
 
 DESCRIPTION:
 
@@ -54,6 +60,9 @@ DESCRIPTION:
   things not statically defined by the NetLogo language, be they user defined
   procedures or generated primitives from breed declarations.
 
+  The possible values for ARG are :agentset, :boolean, :number, :command-block,
+  or t for wildcard.
+
   The need for a parser between the lexer and the transpiler is because NetLogo
   needs two passes to turn into something that can be used.  This is the only entry
   point into this module, and should probably remain that way.
@@ -69,73 +78,110 @@ DESCRIPTION:
   ((*dynamic-prims* dynamic-prims))
   (parse-internal lexed-ast)))
 
-(defun parse-internal (lexed-ast &key prev-item arg-countdown)
+(defun parse-internal (lexed-ast &key prev-item prev-remaining-arg remaining-args)
  (let
   ((prim (and lexed-ast (symbolp (car lexed-ast)) (find-prim (car lexed-ast)))))
   (cond
-   ((and arg-countdown (zerop arg-countdown)) (append (when prev-item (list prev-item)) lexed-ast))
+   ((and remaining-args (eql (car remaining-args) :done-with-args))
+    (append (when prev-item (list (help-arg prev-item prev-remaining-arg))) lexed-ast))
    ((and prim (prim-is-infix prim))
-    (parse-prim prim lexed-ast prev-item arg-countdown)) ; Special casing infix prims is cleaner
+    (parse-prim prim lexed-ast prev-item prev-remaining-arg remaining-args)) ; Special casing infix prims is cleaner
    (t
     (append
-     (when prev-item (list prev-item))
+     (when prev-item (list (help-arg prev-item prev-remaining-arg)))
      (cond
       ((not lexed-ast) nil)
       ((stringp (car lexed-ast))
        (parse-internal (cdr lexed-ast)
         :prev-item (car lexed-ast)
-        :arg-countdown (when arg-countdown (1- arg-countdown))))
+        :prev-remaining-arg (car remaining-args)
+        :remaining-args (cdr remaining-args)))
       ((numberp (car lexed-ast))
        (parse-internal (cdr lexed-ast)
         :prev-item (coerce (car lexed-ast) 'double-float)
-        :arg-countdown (when arg-countdown (1- arg-countdown))))
-      ((eql (intern "(" (find-package :keyword)) (car lexed-ast)) (parse-parened-expr (cdr lexed-ast) arg-countdown))
-      ((eql (intern ")" (find-package :keyword)) (car lexed-ast)) (error "Closing parens has no opening parens"))
-      ((eql :[ (car lexed-ast)) (parse-block (cdr lexed-ast) arg-countdown))
+        :prev-remaining-arg (car remaining-args)
+        :remaining-args (cdr remaining-args)))
+      ((eql (intern "(" :keyword) (car lexed-ast)) (parse-parened-expr (cdr lexed-ast) remaining-args))
+      ((eql (intern ")" :keyword) (car lexed-ast)) (error "Closing parens has no opening parens"))
+      ((eql :let (car lexed-ast)) (parse-let (cdr lexed-ast) remaining-args))
+      ((eql :[ (car lexed-ast)) (parse-block (cdr lexed-ast) remaining-args))
       (prim
        (when (prim-structure-prim prim)
         (error "This doesn't make sense here"))
-       (parse-prim prim lexed-ast nil arg-countdown))
+       (parse-prim prim lexed-ast nil prev-remaining-arg remaining-args))
       (t (error "Couldn't parse ~S" lexed-ast))))))))
 
-(defun parse-prim (prim lexed-ast prev-item arg-countdown)
+(defun parse-let (lexed-ast remaining-args)
+ (when (not (keywordp (car lexed-ast))) (error "Needed a keyword for let"))
  (let*
-  ((num-args (- (prim-num-args prim) (if (prim-is-infix prim) 1 0)))
-   (half-parsed-remainder (parse-internal (cdr lexed-ast) :arg-countdown num-args)))
-  (parse-internal
-   (nthcdr num-args half-parsed-remainder)
-   :arg-countdown (when arg-countdown (if (prim-is-infix prim) arg-countdown (1- arg-countdown)))
-   :prev-item
-   (cons
-    (prim-name prim)
-    (mapcar
-     #'help-arg
-     (prim-args prim)
-     (append
-      (when (prim-is-infix prim) (list prev-item))
-      (butlast half-parsed-remainder (- (length half-parsed-remainder) num-args))))))))
-
-(defun help-arg (arg-type arg)
- (case arg-type
-  (:command-block
+  ((half-parsed-remainder (parse-internal (cdr lexed-ast) :remaining-args (list t :done-with-args))))
+  (let
+   ((*dynamic-prims* (cons (list :name (car lexed-ast)) *dynamic-prims*)))
+   (parse-internal
+    (cdr half-parsed-remainder)
+    :remaining-args (cdr remaining-args)
+    :prev-remaining-arg (car remaining-args)
+    :prev-item (list :let (car lexed-ast) (cadr (car half-parsed-remainder)))))))
+
+(defun parse-prim (prim lexed-ast prev-item prev-remaining-arg remaining-args)
+ (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))))
+   (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))))
+   (middle-forms
+    (cons
+     (cons
+      (prim-name prim)
+      (append
+       (when (prim-is-infix prim) (list (second (help-arg prev-item (car (prim-args prim))))))
+       (mapcar #'cadr (subseq half-parsed-remainder 0 breakpoint))))
+     already-parsed-limbo-forms)))
+  (append
+   (butlast middle-forms)
+   (parse-internal
+    (nthcdr (length args) half-parsed-remainder)
+    :remaining-args (if (prim-is-infix prim) remaining-args (cdr remaining-args))
+    :prev-remaining-arg (if (prim-is-infix prim) prev-remaining-arg (car remaining-args))
+    :prev-item (car (last middle-forms))))))
+
+(defun help-arg (arg arg-type)
+ (cond
+  ((not arg-type) arg)
+  ((eql arg-type :command-block)
    (if (not (and (consp arg) (eql 'block (car arg))))
     (error "Required a block, but found a ~A" arg)
-    (cons :command-block (cdr arg))))
-  (:reporter-block
+    (list :arg (cons :command-block (cdr arg)))))
+  ((eql arg-type :reporter-block)
    (if (not (and (consp arg) (eql 'block (car arg))))
     (error "Required a block, but found a ~A" arg)
-    (cons :reporter-block (cdr arg))))
-  (:list
-   (if (and (consp arg) (eql 'block (car arg)))
-    (cons :list-literal (cdr arg))
-    arg))
-  (t arg)))
-
-(defun parse-block (tokens arg-countdown)
+    (list :arg (cons :reporter-block (cdr arg)))))
+  ((or
+    (eql arg-type :list)
+    (and (listp arg-type) (find :list arg-type)))
+   (list
+    :arg
+    (if (and (consp arg) (eql 'block (car arg)))
+     (cons :list-literal (cdr arg))
+     arg)))
+  ((and
+    (listp arg-type)
+    (find :command-block arg-type)
+    (consp arg)
+    (eql 'block (car arg)))
+   (list :arg (cons :command-block (cdr arg))))
+  ((and (listp arg-type) (find :optional arg-type)) arg)
+  (t (list :arg arg))))
+
+(defun parse-block (tokens remaining-args)
  (multiple-value-bind (in-block after-block) (find-closing-bracket tokens)
   (parse-internal after-block
    :prev-item (cons 'block (parse-internal in-block))
-   :arg-countdown (when arg-countdown (1- arg-countdown)))))
+   :prev-remaining-arg (car remaining-args)
+   :remaining-args (cdr remaining-args))))
 
 (defun find-closing-bracket (tokens &optional (depth 0))
  (cond
@@ -146,7 +192,7 @@ DESCRIPTION:
       (find-closing-bracket (cdr tokens) (case (car tokens) (:[ (1+ depth)) (:] (1- depth)) (t depth)))
       (values (cons (car tokens) in-block) after-block)))))
 
-(defun parse-parened-expr (tokens arg-countdown)
+(defun parse-parened-expr (tokens remaining-args)
  (multiple-value-bind (in-block after-block) (find-closing-paren tokens)
   (parse-internal after-block
    :prev-item
@@ -154,19 +200,20 @@ DESCRIPTION:
     ((parsed-in-block (parse-internal in-block)))
     (when (/= 1 (length parsed-in-block)) (error "Expected ) here"))
     (car parsed-in-block))
-   :arg-countdown (when arg-countdown (1- arg-countdown)))))
+   :prev-remaining-arg (car remaining-args)
+   :remaining-args (cdr remaining-args))))
 
 (defun find-closing-paren (tokens &optional (depth 0))
  (cond
   ((not tokens) (error "Failed to find a matching closing bracket"))
-  ((and (eql (intern ")" (find-package :keyword)) (car tokens)) (= depth 0)) (values nil (cdr tokens)))
+  ((and (eql (intern ")" :keyword) (car tokens)) (= depth 0)) (values nil (cdr tokens)))
   (t (multiple-value-bind
       (in-block after-block)
       (find-closing-paren
        (cdr tokens)
        (cond
-        ((eql (intern "(" (find-package :keyword)) (car tokens)) (1+ depth))
-        ((eql (intern ")" (find-package :keyword)) (car tokens)) (1- depth)) (t depth)))
+        ((eql (intern "(" :keyword) (car tokens)) (1+ depth))
+        ((eql (intern ")" :keyword) (car tokens)) (1- depth)) (t depth)))
       (values (cons (car tokens) in-block) after-block)))))
 
 (defmacro defprim (name args &optional infix)
@@ -200,29 +247,31 @@ DESCRIPTION:
 (defprim :any? (:agentset))
 (defprim :ask (:agentset :command-block))
 (defprim :clear-all ())
-(defprim :crt (:number))
+(defprim :crt (:number (:command-block :optional)))
 (defprim :color ())
-(defprim :count ())
+(defprim :count (:agentset))
 (defprim :die ())
 (defprim :display ())
-(defprim :with (:reporter-block))
+(defprim :with (:agentset :reporter-block) :infix)
 (defprim :fd (:number))
-(defprim :hatch (:number :command-block))
-(defprim :let (t t))
+(defprim :hatch (:number (:command-block :optional)))
+; (defprim :let (t t)) ; keeping this here, commented out, to note that it has special processing
 (defprim :if (:boolean :command-block))
+(defprim :if-else (:boolean :command-block :command-block))
 (defprim :ifelse (:boolean :command-block :command-block))
 (defprim :label ())
 (defprim :label-color ())
 (defprim :not (:boolean))
 (defprim :nobody ())
-(defprim :one-of (t))
+(defprim :one-of ((:agentset :list)))
+(defprim :of (:reporter-block :agentset) :infix)
 (defprim :patches ())
 (defprim :pcolor ())
 (defprim :random (:number))
 (defprim :random-float (:number))
 (defprim :random-xcor ())
 (defprim :random-ycor ())
-(defprim :round ())
+(defprim :round (t))
 (defprim :reset-ticks ())
 (defprim :lt (:number))
 (defprim :rt (:number))
@@ -233,7 +282,9 @@ DESCRIPTION:
 (defprim :size ())
 (defprim :stop ())
 (defprim :tick ())
+(defprim :ticks ())
 (defprim :turtles ())
+(defprim :who ())
 
 ; colors
 (defprim :black ())
@@ -248,41 +299,3 @@ DESCRIPTION:
 (defstructureprim :patches-own)
 (defstructureprim :to)
 (defstructureprim :to-report)
-
-; Placeholder prims that should be populated in dynamic prims
-
-; Generated by globals/widgets
-(defprim :grass ())
-(defprim :initial-number-sheep ())
-(defprim :initial-number-wolves ())
-(defprim :sheep-gain-from-food ())
-(defprim :wolf-gain-from-food ())
-(defprim :sheep-reproduce ())
-(defprim :wolf-reproduce ())
-(defprim :grass? ())
-(defprim :grass-regrowth-time ())
-(defprim :show-energy? ())
-
-; Generated by procedures
-(defprim :move ())
-(defprim :eat-grass ())
-(defprim :reproduce-sheep ())
-(defprim :reproduce-wolves ())
-(defprim :catch-sheep ())
-(defprim :death ())
-(defprim :grow-grass ())
-(defprim :display-labels ())
-
-; Generated by *-own
-(defprim :countdown ())
-(defprim :energy ())
-
-; Generated by a let
-(defprim :prey ())
-
-; Generated by breeds
-(defprim :sheep ())
-(defprim :wolves ())
-(defprim :create-sheep (:number :command-block))   ; look at me not have to do optionals yet
-(defprim :sheep-here ())
-(defprim :create-wolves (:number :command-block))