|
3 | 3 | package org.nlogo.tortoise.compiler |
4 | 4 |
|
5 | 5 | import |
6 | | - org.nlogo.core.{ Application, AstNode, Expression, Instruction, ReporterApp, ReporterBlock, SourceLocatable, |
7 | | - Statement, Syntax } |
| 6 | + org.nlogo.core.{ Application, AstNode, Expression, Instruction, Reporter, ReporterApp, ReporterBlock, |
| 7 | + SourceLocatable, Statement, Syntax } |
8 | 8 |
|
9 | 9 | import |
10 | 10 | org.nlogo.core.prim, |
@@ -101,24 +101,50 @@ object AgentContext { |
101 | 101 | s"PrimChecks.context.assertKind($required, '$primName', ${location.start}, ${location.end});\n$bodyJs" |
102 | 102 | } |
103 | 103 |
|
104 | | - // `ask` rejects only the world's own turtles and patches agentsets, and by identity (`agents eq world.turtles`), |
105 | | - // not by syntax. A `let` variable holding `turtles` is rejected too. So the check is needed whenever the |
106 | | - // expression might be holding one of those sets, but an expression that *builds* an agentset, or that reports a |
107 | | - // single agent, provably can't be one. Anything we can't classify still gets the check. -Jeremy B August 2026 |
| 104 | + // `ask` rejects only the world's own turtles and patches agentsets, and by identity (`agentset == world.turtles()` |
| 105 | + // in desktop's `_ask`), not by syntax. A `let` variable holding `turtles` is rejected too. So the check is needed |
| 106 | + // whenever the expression might be holding one of those two objects, which rules out two whole classes of |
| 107 | + // expression: one that can't report a turtleset or a patchset at all, and one that reports an agentset of its own. |
| 108 | + // Anything we can't classify still gets the check. -Jeremy B August 2026 |
108 | 109 | def canBeWholeAgentSet(exp: Expression): Boolean = |
| 110 | + if ((agentSetTypeOf(exp) & (Syntax.TurtlesetType | Syntax.PatchsetType)) == 0) |
| 111 | + false |
| 112 | + else |
| 113 | + exp match { |
| 114 | + case r: ReporterApp => !reportsAnotherAgentSet(r.reporter) |
| 115 | + case _ => true |
| 116 | + } |
| 117 | + |
| 118 | + // `one-of` is declared as reporting anything, because for a list it does -- including, in principle, a list holding |
| 119 | + // `turtles`. Given an agentset it reports a single agent, and that is the common case worth keeping cheap. |
| 120 | + // -Jeremy B September 2026 |
| 121 | + private def agentSetTypeOf(exp: Expression): Int = |
109 | 122 | exp match { |
110 | | - case r: ReporterApp => |
111 | | - r.reporter match { |
112 | | - case _: prim._breed | _: prim.etc._linkbreed | _: prim.etc._links | |
113 | | - _: prim.etc._turtleset | _: prim.etc._patchset | _: prim.etc._linkset | |
114 | | - _: prim._with | _: prim._other | _: prim._neighbors | |
115 | | - _: prim._neighbors4 | _: prim._turtle | _: prim.etc._patch | |
116 | | - _: prim.etc._link | _: prim._oneof | _: prim.etc._myself | |
117 | | - _: prim.etc._self | _: Optimizer._otherwith | _: Optimizer._oneofwith | |
118 | | - _: Optimizer._patchatreporter => false |
119 | | - case _ => true |
120 | | - } |
121 | | - case _ => true |
| 123 | + case r: ReporterApp if r.reporter.isInstanceOf[prim._oneof] => |
| 124 | + if (r.args.headOption.exists( (a) => (a.reportedType() & Syntax.ListType) != 0 )) |
| 125 | + r.reportedType() |
| 126 | + else |
| 127 | + Syntax.AgentType |
| 128 | + case _ => |
| 129 | + exp.reportedType() |
| 130 | + } |
| 131 | + |
| 132 | + // Prims reporting an agentset that is theirs rather than the world's: a set built on the spot, or a stored one (a |
| 133 | + // breed, `no-turtles`) that is never the identical object `ask` rejects. Only prims that can report a turtleset or |
| 134 | + // a patchset need to be here; the rest are handled by type. -Jeremy B September 2026 |
| 135 | + private def reportsAnotherAgentSet(reporter: Reporter): Boolean = |
| 136 | + reporter match { |
| 137 | + case _: prim._breed | _: prim._breedon | _: prim._inradius | |
| 138 | + _: prim._neighbors | _: prim._neighbors4 | _: prim._other | |
| 139 | + _: prim._turtleson | _: prim._whoarenot | _: prim._with | |
| 140 | + _: prim.etc._atpoints | _: prim.etc._bothends | _: prim.etc._breedat | |
| 141 | + _: prim.etc._breedhere | _: prim.etc._incone | _: prim.etc._maxnof | |
| 142 | + _: prim.etc._inlinkneighbors | _: prim.etc._linkneighbors | _: prim.etc._minnof | |
| 143 | + _: prim.etc._nof | _: prim.etc._nopatches | _: prim.etc._noturtles | |
| 144 | + _: prim.etc._outlinkneighbors | _: prim.etc._patchset | _: prim.etc._turtlesat | |
| 145 | + _: prim.etc._turtleset | _: prim.etc._turtleshere | _: prim.etc._uptonof | |
| 146 | + _: prim.etc._withmax | _: prim.etc._withmin | _: Optimizer._otherwith => true |
| 147 | + case _ => false |
122 | 148 | } |
123 | 149 |
|
124 | 150 | // Can code needing `required` run in `context` without a runtime check? |
|
0 commit comments