1 (in-package #:clnl-code-parser)
3 ; This is different from the general parser (in clnl-parser) in that
4 ; it's made for parsing the code section of nlogo files, and so works
5 ; outside of the constraints. In NetLogo, I believe this is analagous
6 ; to the StructureParser, but I'm guessing there's weird overlap with
9 (defvar *dynamic-prims* nil)
10 (defun global->prim (global) (list :name global))
12 (defun parse (lexed-ast &optional external-globals)
13 "PARSE LEXED-AST &optional EXTERNAL-GLOBALS => AST
17 LEXED-AST: An ambigious ast
18 EXTERNAL-GLOBALS: A list of symbols in keyword package
19 AST: An unambigious ast that represents the code block of a model
23 PARSE takes a ambigious LEXED-AST and converts it to an unambigious one.
25 EXTERNAL-GLOBALS is a list of symbols representing global variables that
26 are not defined within the code. Normally these come from widgets defined
27 in the model file, but could arguably come from elsewhere.
29 This parser, unlike CLNL-PARSE:PARSE, should not be fed into the transpiler.
31 Rather, the ast that's returned can be queried with other functions included
32 in the CLNL-CODE-PARSER package to tease out necessary information. Some of
33 those things will involve code blocks that can then be transpiled."
37 (mapcar #'global->prim external-globals)
38 (procedures->prims lexed-ast))))
39 (parse-internal lexed-ast)))
41 (defun procedures->prims (lexed-ast)
44 ; We'll need argument handling here sometime :)
45 ((eql :to (car lexed-ast)) (cons (list :name (cadr lexed-ast)) (procedures-to-prims (cdr lexed-ast))))
46 (t (procedures-to-prims (cdr lexed-ast)))))
48 (defun parse-internal (lexed-ast)
51 ((eql :to (car lexed-ast)) (parse-procedure lexed-ast))
52 ((find (car lexed-ast) '(:globals :turtles-own :patches-own))
53 (parse-with-unevaluated-list lexed-ast))
54 ((eql (car lexed-ast) :breed) (parse-breed lexed-ast))))
56 ; Due to the non expression style syntax of procedures, this must be special cased
57 (defun parse-procedure (tokens)
58 (multiple-value-bind (in-block after-block) (find-end tokens)
63 (clnl-parser:parse (cddr in-block) *dynamic-prims*))
64 (parse-internal after-block))))
66 (defun find-end (tokens)
68 ((not tokens) (error "Failed to find end"))
69 ((eql :end (car tokens)) (values nil (cdr tokens)))
70 (t (multiple-value-bind (in-block after-block) (find-end (cdr tokens))
71 (values (cons (car tokens) in-block) after-block)))))
73 ; This is a special case but left with a little wiggle room for future
74 ; enhancements, like code blocks
75 (defun parse-with-unevaluated-list (lexed-ast)
76 (when (not (eql :[ (cadr lexed-ast))) (error "Expected list literal here"))
77 (multiple-value-bind (in-list after-list) (find-closing-bracket (cddr lexed-ast))
79 (list (car lexed-ast) (cons :list-literal in-list))
81 ((*dynamic-prims* (append (mapcar #'global->prim in-list) *dynamic-prims*)))
82 (parse-internal after-list)))))
84 (defun parse-breed (lexed-ast)
85 (when (not (eql :[ (cadr lexed-ast))) (error "Expected list literal here"))
86 (multiple-value-bind (in-list after-list) (find-closing-bracket (cddr lexed-ast))
88 (list (car lexed-ast) (cons :list-literal in-list))
89 (parse-internal after-list))))
91 (defun find-closing-bracket (tokens)
93 ((not tokens) (error "Failed to find a matching closing bracket"))
94 ((eql :] (car tokens)) (values nil (cdr tokens)))
95 ((eql :[ (car tokens)) (error "Expected name or ]"))
96 (t (multiple-value-bind (in-block after-block) (find-closing-bracket (cdr tokens))
97 (values (cons (car tokens) in-block) after-block)))))
99 (defun globals (code-parsed-ast)
100 "GLOBALS MODEL => GLOBALS
104 ARGUMENTS AND VALUES:
106 MODEL: An ast as created by clnl-code-parse:parse
107 GLOBAL: A symbol interned in clnl:*model-package*
111 Returns the globals that get declared in the code."
113 (lambda (global) (list (symbol-name global) 0d0))
114 (cdr (second (find :globals code-parsed-ast :key #'car)))))