Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/origin/master' into wip-tortuga
Browse files Browse the repository at this point in the history
  • Loading branch information
John Chen committed May 27, 2024
2 parents 7f7e934 + 89704e0 commit a6f1745
Show file tree
Hide file tree
Showing 15 changed files with 141 additions and 49 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import sbtcrossproject.CrossPlugin.autoImport.CrossType
import sbtcrossproject.CrossProject
import org.scalajs.sbtplugin.ScalaJSCrossVersion

val nlDependencyVersion = "6.4.0"
val nlDependencyVersion = "6.4.0-e625bb1"

val parserJsDependencyVersion = "0.4.0-78e5f87"
val parserJsDependencyVersion = "0.4.0-e625bb1"

val scalazVersion = "7.2.35"

Expand Down
12 changes: 6 additions & 6 deletions compiler/shared/src/main/scala/MultiAssignTransformer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@
package org.nlogo.tortoise.compiler

import org.nlogo.core.{ AstTransformer, ProcedureDefinition, ReporterApp, Statement, Statements }
import org.nlogo.core.prim.{ _let, _multiletitem }
import org.nlogo.core.prim.{ _let, _multiassignitem }

object MultiAssignTransformer {
// NetLogo desktop creates synthetic let statements for _multilet that get their values from an intermediate construct
// that holds the list of values to be assigned. This is necessary there because they can't translate it to a real
// destructured assignment. In JavaScript we can use a destructured assignment language construct, so we have no need
// of the synthetic extras. So we sot them. -Jeremy B November 2023
// NetLogo desktop creates synthetic let statements for _multiassign that get their values from an intermediate
// construct that holds the list of values to be assigned. This is necessary there because they can't translate it to
// a real destructured assignment. In JavaScript we can use a destructured assignment language construct, so we have
// no need of the synthetic extras. So we sot them. -Jeremy B November 2023
object MultiLetRemover extends AstTransformer {
private def dropMultiLetStmts(stmts: Seq[Statement]): Seq[Statement] = {
val results = stmts.filter( (s) => {
!(s.command.isInstanceOf[_let] &&
s.args.length == 1 &&
s.args(0).isInstanceOf[ReporterApp] &&
s.args(0).asInstanceOf[ReporterApp].reporter.isInstanceOf[_multiletitem])
s.args(0).asInstanceOf[ReporterApp].reporter.isInstanceOf[_multiassignitem])
})
results
}
Expand Down
42 changes: 40 additions & 2 deletions compiler/shared/src/main/scala/Optimizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import

import
org.nlogo.core.{ prim, AstTransformer, ProcedureDefinition, ReporterApp, Statement, NetLogoCore, CommandBlock, ReporterBlock },
prim.{ _any, _const, _count, _createorderedturtles, _createturtles, _equal, _fd, _greaterthan, _hatch, _lessthan,
prim.{ _any, _breedon, _const, _count, _createorderedturtles, _createturtles, _equal, _fd, _greaterthan, _hatch, _lessthan,
_neighbors, _neighbors4, _not, _notequal, _observervariable, _of, _oneof, _other, _patchat, _patches, _patchvariable,
_procedurevariable, _random, _sprout, _sum, _with }
_procedurevariable, _random, _sprout, _sum, _turtleson, _with }

// scalastyle:off number.of.methods
object Optimizer {
Expand Down Expand Up @@ -333,6 +333,42 @@ object Optimizer {
}
}

class _anyturtleson extends Reporter {
override def syntax: Syntax =
Syntax.reporterSyntax(right = List(Syntax.AgentsetType | Syntax.AgentType), ret = Syntax.BooleanType)
}

// _any(_turtleson) => _anyturtleson
object AnyTurtlesOnTransformer extends AstTransformer {
override def visitReporterApp(ra: ReporterApp): ReporterApp = {
ra match {
case ReporterApp(_: _any, Seq(ReporterApp(to: _turtleson, turtlesonArgs, _)), _) =>
val r = new _anyturtleson
r.token = to.token
ra.copy(reporter = r, args = turtlesonArgs)
case _ => super.visitReporterApp(ra)
}
}
}

class _anybreedon(val breedName: String) extends Reporter {
override def syntax: Syntax =
Syntax.reporterSyntax(right = List(Syntax.AgentsetType | Syntax.AgentType), ret = Syntax.BooleanType)
}

// _any(_breedon) => _anybreedon
object AnyBreedOnTransformer extends AstTransformer {
override def visitReporterApp(ra: ReporterApp): ReporterApp = {
ra match {
case ReporterApp(_: _any, Seq(ReporterApp(to: _breedon, breedonArgs, _)), _) =>
val r = new _anybreedon(to.breedName)
r.token = to.token
ra.copy(reporter = r, args = breedonArgs)
case _ => super.visitReporterApp(ra)
}
}
}

class NeighborTransformer extends AstTransformer {
def visitNeighbor(ra: ReporterApp, check: Reporter => Boolean, make: String => Reporter): ReporterApp = {
ra match {
Expand Down Expand Up @@ -495,6 +531,8 @@ object Optimizer {
AnyWith3Transformer .visitProcedureDefinition andThen
AnyWith4Transformer .visitProcedureDefinition andThen
AnyWith5Transformer .visitProcedureDefinition andThen
AnyTurtlesOnTransformer .visitProcedureDefinition andThen
AnyBreedOnTransformer .visitProcedureDefinition andThen
OptimizeCountTransformer .visitProcedureDefinition andThen
RandomConstTransformer .visitProcedureDefinition
)(pd)
Expand Down
6 changes: 3 additions & 3 deletions compiler/shared/src/main/scala/Prims.scala
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,8 @@ trait ReporterPrims extends PrimUtils {
case p: prim.etc._linkbreedsingular => s"world.linkManager.getLink(${args.get(0)}, ${args.get(1)}, ${jsString(p.breedName)})"
case b: prim.etc._breedat => s"SelfManager.self().breedAt(${jsString(b.breedName)}, ${args.get(0)}, ${args.get(1)})"
case b: prim.etc._breedhere => s"SelfManager.self().breedHere(${jsString(b.breedName)})"
case b: prim.etc._breedon => s"PrimChecks.agentset.breedOn(${jsString(b.breedName)}, ${args.makeCheckedOp(0)})"
case b: prim._breedon => s"PrimChecks.agentset.breedOn(${jsString(b.breedName)}, ${args.makeCheckedOp(0)})"
case b: Optimizer._anybreedon => s"PrimChecks.agentset.anyBreedOn(${jsString(b.breedName)}, ${args.makeCheckedOp(0)})"
case b: prim.etc._isbreed => s"NLType.checks.isBreed(${jsString(b.breedName)}, ${args.get(0)})"

// List prims
Expand Down Expand Up @@ -253,8 +254,7 @@ trait ReporterPrims extends PrimUtils {
case ra: prim.etc._range =>
generateRange(sourceInfo.start, sourceInfo.end, useCompileArgs, args.checked, ra.syntax)

// This is very unfortunately named, since in this case it's used for `set`, not `let`. Ugh. -Jeremy B December 2023
case mli: prim._multiletitem =>
case mli: prim._multiassignitem =>
s"__MULTI_SET_ARRAY.shift()"

case _ if compilerFlags.generateUnimplemented =>
Expand Down
7 changes: 4 additions & 3 deletions compiler/shared/src/main/scala/SimplePrims.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,10 @@ object SimplePrims {
PartialFunction.condOpt(r) {

// Agentset
case _: prim._any => "PrimChecks.agentset.any"
case _: prim._count => "PrimChecks.agentset.count"
case _: prim.etc._turtleson => "PrimChecks.agentset.turtlesOn"
case _: prim._any => "PrimChecks.agentset.any"
case _: prim._count => "PrimChecks.agentset.count"
case _: prim._turtleson => "PrimChecks.agentset.turtlesOn"
case _: Optimizer._anyturtleson => "PrimChecks.agentset.anyTurtlesOn"

// List / String
case _: prim.etc._empty => "PrimChecks.list.empty"
Expand Down
8 changes: 8 additions & 0 deletions engine/src/main/coffee/engine/core/patch.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ module.exports =
breedHereArray: (breedName) ->
filter((turtle) -> turtle.getBreedName() is breedName)(@_turtles)

# (String) => Boolean
anyBreedHere: (breedName) ->
for turtle in @_turtles
if turtle.getBreedName() is breedName
return true

false

# (Number, Number) => TurtleSet
turtlesAt: (dx, dy) ->
@patchAt(dx, dy).turtlesHere()
Expand Down
8 changes: 8 additions & 0 deletions engine/src/main/coffee/engine/core/turtle.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,14 @@ module.exports =
breedHere: (breedName) ->
@getPatchHere().breedHere(breedName)

# (String) => Array[Turtle]
breedHereArray: (breedName) ->
@getPatchHere().breedHereArray(breedName)

# (String) => Boolean
anyBreedHere: (breedName) ->
@getPatchHere().anyBreedHere(breedName)

# (Number, String) => TurtleSet
hatch: (n, breedName) ->
num = if n >= 0 then n else 0
Expand Down
26 changes: 18 additions & 8 deletions engine/src/main/coffee/engine/prim-checks/agentset-checks.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,17 @@ class AgentSetChecks

# (String, Patch | Turtle | PatchSet | TurtleSet) => AgentSet
breedOn: (breedName, target) ->
if checks.isPatch(target)
@prims.breedOnPatch(breedName, target)
else if checks.isTurtle(target)
@prims.breedOnTurtle(breedName, target)
else if checks.isPatchSet(target)
@prims.breedOnPatchSet(breedName, target)
else if checks.isTurtleSet(target)
@prims.breedOnTurtleSet(breedName, target)
if checks.isAgentSet(target)
@prims.breedOnAgentSet(breedName, target)
else
@prims.breedOnAgent(breedName, target)

# (String, Patch | Turtle | PatchSet | TurtleSet) => Boolean
anyBreedOn: (breedName, target) ->
if checks.isAgentSet(target)
@prims.anyBreedOnAgentSet(breedName, target)
else
@prims.anyBreedOnAgent(breedName, target)

# (AgentSet[T]) => Number
count: (agentset) ->
Expand Down Expand Up @@ -199,6 +202,13 @@ class AgentSetChecks
else
@prims.turtlesOnAgent(agentOrAgentset)

# (Agent | AgentSet) => Boolean
anyTurtlesOn: (agentOrAgentset) ->
if checks.isAgentSet(agentOrAgentset)
@prims.anyTurtlesOnAgentSet(agentOrAgentset)
else
@prims.anyTurtlesOnAgent(agentOrAgentset)

# [T <: (Array[Turtle]|Turtle|AbstractAgentSet[Turtle])] @ (Int, Int, T*) => TurtleSet
turtleSet: (sourceStart, sourceEnd, values...) ->
@setCreationArgsCheck('turtle-set', sourceStart, sourceEnd, types.Turtle, types.TurtleSet, values)
Expand Down
60 changes: 44 additions & 16 deletions engine/src/main/coffee/engine/prim/prims.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,37 @@ module.exports =
boom: ->
throw exceptions.runtime("boom!", "boom")

# (String, Array[Patch]) -> TurtleSet
breedOn: (breedName, patches) ->
turtles = flatMap((p) -> p.breedHereArray(breedName))(patches)
# (String, Agent) -> TurtleSet
breedOnAgent: (breedName, agent) ->
turtles = agent.breedHereArray(breedName)
new TurtleSet(turtles, @_world)

# (String, Patch) => TurtleSet
breedOnPatch: (breedName, patch) ->
@breedOn(breedName, [patch])
# (String, Turtle | Patch) -> TurtleSet
anyBreedOnAgent: (breedName, agent) ->
agent.anyBreedHere(breedName)

# (String, Turtle) => TurtleSet
breedOnTurtle: (breedName, turtle) ->
@breedOn(breedName, [turtle.getPatchHere()])
# (String, AgentSet) -> TurtleSet
breedOnAgentSet: (breedName, agents) ->
turtles = flatMap((p) -> p.breedHereArray(breedName))(agents.toArray())
new TurtleSet(turtles, @_world)

# (String, TurtleSet | PatchSet) -> Boolean
anyBreedOnAgentSet: (breedName, agents) ->
if checks.isPatchSet(agents)
for patch in agents.toArray()
if patch.anyBreedHere(breedName)
return true

# (String, PatchSet) => TurtleSet
breedOnPatchSet: (breedName, patchSet) ->
@breedOn(breedName, patchSet.toArray())
else
seenPatches = []
for turtle in agents.toArray()
patch = turtle.getPatchHere()
if not seenPatches.includes(patch)
seenPatches.push(patch)
if patch.anyBreedHere(breedName)
return true

# (String, TurtleSet) => TurtleSet
breedOnTurtleSet: (breedName, turtleSet) ->
@breedOn(breedName, map((t) -> t.getPatchHere())(turtleSet.iterator().toArray()))
false

# (Any, String) => Boolean
booleanCheck: (b, primName) ->
Expand Down Expand Up @@ -232,10 +243,27 @@ module.exports =
turtlesOnAgent: (agent) ->
agent.turtlesHere()

# (Patch|Turtle) => Boolean
anyTurtlesOnAgent: (agent) ->
checks.isTurtle(agent) or not agent.turtlesHere().isEmpty()

# (PatchSet|TurtleSet) => TurtleSet
turtlesOnAgentSet: (agents) ->
turtles = flatMap((agent) -> agent.turtlesHere().toArray())(agents.iterator().toArray())
new TurtleSet(turtles, @_world)
new TurtleSet(Array.from(new Set(turtles)), @_world)

# (PatchSet|TurtleSet) => Boolean
anyTurtlesOnAgentSet: (agents) ->
if checks.isTurtleSet(agents)
return not agents.isEmpty()

# This is intentionally written as a loop for the `_anyturtleson` compiler optimization early exit -Jeremy B March
# 2024
for agent in agents
if not agent.turtlesHere().isEmpty()
return true

false

_hasWaited: false

Expand Down
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ resolvers += "netlogo-publish-versioned" at "https://dl.cloudsmith.io/public/net

addSbtPlugin("org.nlogo" % "publish-versioned-plugin" % "3.0.0")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.12.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.0")
addSbtPlugin("org.scala-js" % "sbt-jsdependencies" % "1.0.2")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.0")
1 change: 1 addition & 0 deletions resources/scripts/run-quick-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

time ./sbt.sh \
netLogoWeb/scalastyle compilerCore/scalastyle compilerJVM/scalastyle compilerJS/scalastyle macrosCore/scalastyle \
"engine / coffeelint" \
"compilerJS / Test / compile" \
"compilerJS / Test / test" \
"compilerJVM / Test / compile" \
Expand Down
4 changes: 2 additions & 2 deletions resources/test/dumps/Dice Stalagmite.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions resources/test/dumps/Heatbugs Benchmark.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion resources/test/dumps/Life Turtle-Based.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 1 addition & 3 deletions resources/test/dumps/Traffic Grid.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a6f1745

Please sign in to comment.