From d4982c838e031e90a4fd74906363e3f5b1a72545 Mon Sep 17 00:00:00 2001 From: Karl Ostmo Date: Sun, 5 May 2024 18:29:16 -0700 Subject: [PATCH] fix schema renderer (#1823) Was crashing because `enum` type was not supported. # Test ``` scripts/gen/schema-docs.sh ``` [New generated doc](https://github.com/swarm-game/swarm/blob/ebe28c3a781fded37024fce8d49b4ecb95e1577f/data/scenarios/_doc-fragments/SCHEMA.md#directions) now contains "Directions" enum. --- app/doc/Swarm/Doc/Schema/Parse.hs | 3 + app/doc/Swarm/Doc/Schema/Refined.hs | 4 + app/doc/Swarm/Doc/Schema/Render.hs | 6 + app/doc/Swarm/Doc/Schema/SchemaType.hs | 2 + data/scenarios/_doc-fragments/SCHEMA.md | 184 ++++++++++++++---------- 5 files changed, 120 insertions(+), 79 deletions(-) diff --git a/app/doc/Swarm/Doc/Schema/Parse.hs b/app/doc/Swarm/Doc/Schema/Parse.hs index 8f465c739..13b05d2ba 100644 --- a/app/doc/Swarm/Doc/Schema/Parse.hs +++ b/app/doc/Swarm/Doc/Schema/Parse.hs @@ -11,6 +11,7 @@ module Swarm.Doc.Schema.Parse where import Control.Applicative ((<|>)) import Data.Aeson +import Data.List.NonEmpty (NonEmpty) import Data.Map (Map) import Data.Maybe (fromMaybe) import Data.Text (Text) @@ -28,6 +29,7 @@ data SchemaData = SchemaData data Members = ObjectProperties (Map Text SwarmSchema) | ListMembers (ItemDescription SwarmSchema) + | EnumMembers (NonEmpty Text) deriving (Eq, Ord, Show) data ToplevelSchema = ToplevelSchema @@ -49,4 +51,5 @@ instance FromJSON ToplevelSchema where maybeMembers = ObjectProperties <$> properties swarmSchema <|> ListMembers <$> itemsDescription swarmSchema + <|> EnumMembers <$> _enum rawSchema return $ ToplevelSchema theTitle (objectDescription swarmSchema) swarmSchema maybeMembers theFooters diff --git a/app/doc/Swarm/Doc/Schema/Refined.hs b/app/doc/Swarm/Doc/Schema/Refined.hs index afaee625c..0968d01bd 100644 --- a/app/doc/Swarm/Doc/Schema/Refined.hs +++ b/app/doc/Swarm/Doc/Schema/Refined.hs @@ -11,6 +11,7 @@ module Swarm.Doc.Schema.Refined where import Control.Applicative ((<|>)) import Data.Aeson import Data.List.Extra (replace) +import Data.List.NonEmpty (NonEmpty) import Data.Map (Map) import Data.Map qualified as M import Data.Maybe (fromMaybe, mapMaybe) @@ -47,6 +48,7 @@ data SchemaRaw = SchemaRaw , _oneOf :: Maybe [SchemaRaw] , _footers :: Maybe [FilePath] , _additionalProperties :: Maybe Bool + , _enum :: Maybe (NonEmpty Text) } deriving (Eq, Ord, Show, Generic) @@ -59,6 +61,7 @@ extractSchemaType rawSchema = <|> getTypeFromItems <|> Simple <$> _type rawSchema <|> Alternatives . mapMaybe extractSchemaType <$> _oneOf rawSchema + <|> EnumList <$> _enum rawSchema where mkReference = Reference . SchemaIdReference . T.pack . takeBaseName . T.unpack @@ -87,6 +90,7 @@ getSchemaReferences = \case Alternatives xs -> concatMap getSchemaReferences xs Reference x -> pure x ListOf x -> getSchemaReferences x + EnumList _ -> [] -- | A subset of all JSON schemas, conforming to internal Swarm conventions. -- diff --git a/app/doc/Swarm/Doc/Schema/Render.hs b/app/doc/Swarm/Doc/Schema/Render.hs index 24a3c2518..742515f9d 100644 --- a/app/doc/Swarm/Doc/Schema/Render.hs +++ b/app/doc/Swarm/Doc/Schema/Render.hs @@ -14,6 +14,7 @@ import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Except (except) import Data.Aeson import Data.List (intersperse) +import Data.List.NonEmpty qualified as NE import Data.Map (Map) import Data.Map.Strict qualified as M import Data.Maybe (fromMaybe) @@ -81,6 +82,10 @@ makePandocTable titleMap (SchemaData _ (ToplevelSchema theTitle theDescription _ mkTable x = doc $ case x of ObjectProperties props -> makePropsTable True propertyColumnHeadings titleMap props ListMembers someStuff -> renderItems someStuff + EnumMembers enumMembers -> + simpleTable [plain $ text "Member"] $ + map (\m -> [plain $ code m]) $ + NE.toList enumMembers genPropsRow :: Bool -> Map SchemaIdReference T.Text -> (T.Text, SwarmSchema) -> [Blocks] genPropsRow includeDefaultColumn titleMap (k, x) = @@ -176,6 +181,7 @@ listToText titleMap = \case Alternatives xs -> renderAlternatives $ map (listToText titleMap) xs Reference r@(SchemaIdReference x) -> schemaLink r x ListOf x -> listToText titleMap x <> text " list" + EnumList xs -> renderAlternatives $ NE.toList $ text <$> xs where renderAlternatives = mconcat . intersperse (text " or ") schemaLink r = link (fragmentHref titleMap r) "Link to object properties" . text diff --git a/app/doc/Swarm/Doc/Schema/SchemaType.hs b/app/doc/Swarm/Doc/Schema/SchemaType.hs index 4f85397ef..f6a9a57ef 100644 --- a/app/doc/Swarm/Doc/Schema/SchemaType.hs +++ b/app/doc/Swarm/Doc/Schema/SchemaType.hs @@ -6,6 +6,7 @@ module Swarm.Doc.Schema.SchemaType where import Control.Applicative ((<|>)) import Data.Aeson +import Data.List.NonEmpty (NonEmpty) import Data.Text (Text) import Data.Text qualified as T import System.FilePath (takeBaseName) @@ -29,6 +30,7 @@ data SchemaType Reference SchemaIdReference | -- | Members of a list, all of the given schema type ListOf SchemaType + | EnumList (NonEmpty Text) deriving (Eq, Ord, Show) newtype SchemaIdReference = SchemaIdReference Text diff --git a/data/scenarios/_doc-fragments/SCHEMA.md b/data/scenarios/_doc-fragments/SCHEMA.md index d61ddaba1..b2f2a3803 100644 --- a/data/scenarios/_doc-fragments/SCHEMA.md +++ b/data/scenarios/_doc-fragments/SCHEMA.md @@ -35,6 +35,7 @@ by the following table. | `stepsPerTick` | | `number` | When present, this specifies the maximum number of CESK machine steps each robot is allowed to take per game tick. It is rather obscure and technical and only used in a few automated tests; most scenario authors should not need this. | | `structures` | | [named-structure](#named-structure "Link to object properties") list | Structure definitions | | `subworlds` | | [world](#world "Link to object properties") list | A list of subworld definitions | +| `terrains` | `[]` | [terrain](#terrain "Link to object properties") list | An optional list of custom terrain, to be used in addition to the built-in terrain. | | `version` | | `number` | The version number of the scenario schema. Currently, this should always be `1`. | | `world` | | [world](#world "Link to object properties") | | @@ -55,7 +56,8 @@ Description of an entity in the Swarm game | Key | Default? | Type | Description | |----------------|----------|-------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `capabilities` | `[]` | `string` list | A list of capabilities provided by entity, when it is equipped as a device. See [Capabilities](https://github.com/swarm-game/swarm/wiki/Capabilities-cheat-sheet). | +| `biomes` | `[]` | `string` list | A list of terrains that support growth by this entity. Empty list means no growth restrictions. | +| `capabilities` | `[]` | `string` or `object` list | A list of capabilities provided by entity, when it is equipped as a device. See [Capabilities](https://github.com/swarm-game/swarm/wiki/Capabilities-cheat-sheet). | | `combustion` | | [combustion](#combustion "Link to object properties") | Properties of combustion. | | `description` | | `string` list | A description of the entity, as a list of paragraphs. | | `display` | | [display](#display "Link to object properties") | Display information for the entity. | @@ -64,6 +66,7 @@ Description of an entity in the Swarm game | `orientation` | | `array` | A 2-tuple of integers specifying an orientation vector for the entity. Currently unused. | | `plural` | | `string` | An explicit plural form of the name of the entity. If omitted, standard heuristics will be used for forming the English plural of its name. | | `properties` | `[]` | `string` list | A list of properties of this entity. | +| `tags` | | `string` list | A list of categories this entity belongs to. | | `yields` | | `string` | The name of the entity which will be added to a robot's inventory when it executes grab or harvest on this entity. If omitted, the entity will simply yield itself. | #### Entity properties @@ -109,19 +112,19 @@ Properties of entity combustion Description of a robot in the Swarm game -| Key | Default? | Type | Description | -|---------------|-------------|--------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `description` | | `string` | A description of the robot. This is currently not used for much, other than scenario documentation. | -| `devices` | `[]` | `string` list | A list of entity names which should be equipped as the robot's devices, i.e. entities providing capabilities to run commands and interpret language constructs. | -| `dir` | `[0, 0]` | `array` | An optional starting orientation of the robot, expressed as a vector. Every time the robot executes a `move` command, this vector will be added to its position. Typically, this is a unit vector in one of the four cardinal directions, although there is no particular reason that it has to be. When omitted, the robot's direction will be the zero vector. | -| `display` | `"default"` | [display](#display "Link to object properties") | Display information for the robot. If this field is omitted, the default robot display will be used. | -| `heavy` | `False` | `boolean` | Whether the robot is heavy. Heavy robots require `tank treads` to `move` (rather than just `treads` for other robots). | -| `inventory` | `[]` | [inventory](#inventory "Link to object properties") | A list of `[count, entity name]` pairs, specifying the entities in the robot's starting inventory, and the number of each. | -| `loc` | | [cosmic-loc](#cosmic-location "Link to object properties") or [planar-loc](#planar-location "Link to object properties") | An optional starting location for the robot. If the `loc` field is specified, then a concrete robot will be created at the given location. If this field is omitted, then this robot record exists only as a template which can be referenced from a cell in the world palette. Concrete robots will then be created wherever the corresponding palette character is used in the world map. | -| `name` | | `string` | The name of the robot. This shows up in the list of robots in the game (`F2`), and is also how the robot will be referred to in the world palette. | -| `program` | | `string` | This is the text of a Swarm program which the robot should initially run, and must be syntax- and type-error-free. If omitted, the robot will simply be idle. | -| `system` | `False` | `boolean` | Whether the robot is a "system" robot. System robots can do anything, without regard for devices and capabilities. System robots are invisible by default. | -| `unwalkable` | `[]` | `string` list | A list of entities that this robot cannot walk across. | +| Key | Default? | Type | Description | +|---------------|-------------|--------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `description` | | `string` | A description of the robot. This is currently not used for much, other than scenario documentation. | +| `devices` | `[]` | `string` list | A list of entity names which should be equipped as the robot's devices, i.e. entities providing capabilities to run commands and interpret language constructs. | +| `dir` | | `array` or [direction](#directions "Link to object properties") | An optional starting orientation of the robot, expressed as either (1) the name of a cardinal direction or (2) a vector. Every time the robot executes a `move` command, this vector will be added to its position. Typically, this is a unit vector in one of the four cardinal directions, although there is no particular reason that it has to be. When omitted, the robot's direction will be the zero vector. | +| `display` | `"default"` | [display](#display "Link to object properties") | Display information for the robot. If this field is omitted, the default robot display will be used. | +| `heavy` | `False` | `boolean` | Whether the robot is heavy. Heavy robots require `tank treads` to `move` (rather than just `treads` for other robots). | +| `inventory` | `[]` | [inventory](#inventory "Link to object properties") | A list of `[count, entity name]` pairs, specifying the entities in the robot's starting inventory, and the number of each. | +| `loc` | | [cosmic-loc](#cosmic-location "Link to object properties") or [planar-loc](#planar-location "Link to object properties") | An optional starting location for the robot. If the `loc` field is specified, then a concrete robot will be created at the given location. If this field is omitted, then this robot record exists only as a template which can be referenced from a cell in the world palette. Concrete robots will then be created wherever the corresponding palette character is used in the world map. | +| `name` | | `string` | The name of the robot. This shows up in the list of robots in the game (`F2`), and is also how the robot will be referred to in the world palette. | +| `program` | | `string` | This is the text of a Swarm program which the robot should initially run, and must be syntax- and type-error-free. If omitted, the robot will simply be idle. | +| `system` | `False` | `boolean` | Whether the robot is a "system" robot. System robots can do anything, without regard for devices and capabilities. System robots are invisible by default. | +| `walkable` | | `object` | Blacklist/whitelist of walkable entities | #### Base robot @@ -149,48 +152,6 @@ Planar location plus subworld | `loc` | | [planar-loc](#planar-location "Link to object properties") | | | `subworld` | | `string` | Name of subworld | -### Display - -Swarm entity display. A display specifies how an entity or a robot -(robots are essentially special kinds of entities) is displayed in the -world. It consists of a key-value mapping described by the following -table. - -| Key | Default? | Type | Description | -|------------------|---------------|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `attr` | `"entity"` | `string` | The name of the attribute that should be used to style the robot or entity. A list of currently valid attributes can be found [here](https://github.com/swarm-game/swarm/blob/main/src/Swarm/TUI/View/Attribute/Attr.hs). | -| `char` | `" "` | `string` | The default character that should be used to draw the robot or entity. | -| `curOrientation` | | `array` | Currently unused | -| `invisible` | `False` | `boolean` | Whether the entity or robot should be invisible. Invisible entities and robots are not drawn, but can still be interacted with in otherwise normal ways. System robots are by default invisible. | -| `orientationMap` | `fromList []` | [orientation-map](#orientation-map "Link to object properties") | | -| `priority` | `1` | `number` | When multiple entities and robots occupy the same cell, the one with the highest priority is drawn. By default, entities have priority `1`, and robots have priority `10`. | - -### Recipe - -Recipe describes a process that takes some inputs and produces some -outputs, which robots can access using `make` and `drill`. - -| Key | Default? | Type | Description | -|------------|----------|-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `in` | | [inventory](#inventory "Link to object properties") | A list of ingredients consumed by the recipe. Each ingredient is a tuple consisting of an integer and an entity name, indicating the number of copies of the given entity that are needed. | -| `out` | | [inventory](#inventory "Link to object properties") | A list of outputs produced by the recipe. It is a list of `[count, entity name]` tuples just like `in`. | -| `required` | `[]` | [inventory](#inventory "Link to object properties") | A list of catalysts required by the recipe. They are neither consumed nor produced, but must be present in order for the recipe to be carried out. It is a list of \[count, entity name\] tuples just like in and out. | -| `time` | `1` | `number` | The number of ticks the recipe takes to perform. For recipes which take more than 1 tick, the robot will wait for a number of ticks until the recipe is complete. For example, this is used for many drilling recipes. | -| `weight` | `1` | `number` | Whenever there are multiple recipes that match the relevant criteria, one of them will be chosen at random, with probability proportional to their weights. For example, suppose there are two recipes that both output a widget, one with weight `1` and the other with weight `9`. When a robot executes `make "widget"`, the first recipe will be chosen 10% of the time, and the second recipe 90% of the time. | - -### Inventory - -List of [entity-count](#entity-count "Link to object properties") - -### Entity count - -One row in an inventory list - -| Index | Type | Description | -|-------|----------|-------------| -| `0` | `number` | Quantity | -| `1` | `string` | Entity name | - ### World Description of the world in the Swarm game @@ -203,7 +164,7 @@ Description of the world in the Swarm game | `name` | | `string` | Name of this subworld | | `offset` | `False` | `boolean` | Whether the base robot's position should be moved to the nearest "good" location, currently defined as a location near a `tree`, in a 16x16 patch which contains at least one each of `tree`, `copper ore`, `bit (0)`, `bit (1)`, `rock`, `lambda`, `water`, and `sand`. The classic scenario uses `offset: True` to make sure that the it is not unreasonably difficult to obtain necessary resources in the early game (see [code](https://github.com/swarm-game/swarm/blob/e06e04f622a3762a10e7c942c1cbd2c1e396144f/src/Swarm/Game/World/Gen.hs#L79)). | | `palette` | | `object` | The palette maps single character keys to tuples representing contents of cells in the world, so that a world containing entities and robots can be drawn graphically. See [Cells](#cells) for the contents of the tuples representing a cell. | -| `placements` | | [placement](#placement "Link to object properties") list | Structure placements | +| `placements` | | [placement](#placement "Link to object properties") list | Structure placements. Earlier members may occlude later members of the list. | | `portals` | | [portal](#portal "Link to object properties") list | A list of portal definitions that reference waypoints. | | `scrollable` | `True` | `boolean` | Whether players are allowed to scroll the world map. | | `structures` | | [named-structure](#named-structure "Link to object properties") list | Structure definitions | @@ -240,24 +201,98 @@ robot. A 2-tuple specifies a terrain value and entity, but no robot. Structure definitions -| Key | Default? | Type | Description | -|-------------|----------|-----------------------------------------------------|---------------------------| -| `name` | | `string` | Name of this substructure | -| `structure` | | [structure](#structure "Link to object properties") | | +| Key | Default? | Type | Description | +|---------------|----------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------| +| `description` | | `string` | Description of this substructure | +| `name` | | `string` | Name of this substructure | +| `recognize` | | [direction](#directions "Link to object properties") list | Orientations for which this structure participates in automatic recognition when constructed | +| `structure` | | [structure](#structure "Link to object properties") | | ### Structure -Structure properties +Structure properties. Structures may opt-in to "automatic recognition" +for when they are constructed by a robot. There are certain limitations +on the shape and placement of such "recognizable" structures. | Key | Default? | Type | Description | |--------------|----------|----------------------------------------------------------------------|--------------------------------------------------------------------------------| | `map` | | `string` | Cell-based representation of the structure using palette entries | | `mask` | | `string` | A special palette character that indicates that map cell should be transparent | | `palette` | | `object` | Structure properties | -| `placements` | | [placement](#placement "Link to object properties") list | Structure placements | +| `placements` | | [placement](#placement "Link to object properties") list | Structure placements. Earlier members may occlude later members of the list. | | `structures` | | [named-structure](#named-structure "Link to object properties") list | Nested structure definitions | | `waypoints` | | [explicit-waypoint](#waypoint "Link to object properties") list | Single-location waypoint definitions | +### Placement + +Structure placement + +| Key | Default? | Type | Description | +|----------|----------|------------------------------------------------------------------------|------------------------------| +| `offset` | | [planar-loc](#planar-location "Link to object properties") | | +| `orient` | | [structure-orient](#structure-orientation "Link to object properties") | | +| `src` | | `string` | Name of structure definition | + +### Structure orientation + +Structure orientation properties + +| Key | Default? | Type | Description | +|--------|----------|------------------------------------------------------|-------------| +| `flip` | | `boolean` | | +| `up` | | [direction](#directions "Link to object properties") | | + +### Directions + +| Member | +|---------| +| `north` | +| `west` | +| `south` | +| `east` | + +### Display + +Swarm entity display. A display specifies how an entity or a robot +(robots are essentially special kinds of entities) is displayed in the +world. It consists of a key-value mapping described by the following +table. + +| Key | Default? | Type | Description | +|------------------|---------------|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `attr` | `"entity"` | `string` | The name of the attribute that should be used to style the robot or entity. A list of currently valid attributes can be found [here](https://github.com/swarm-game/swarm/blob/main/src/Swarm/TUI/View/Attribute/Attr.hs). | +| `char` | `" "` | `string` | The default character that should be used to draw the robot or entity. | +| `curOrientation` | | `array` | Currently unused | +| `invisible` | `False` | `boolean` | Whether the entity or robot should be invisible. Invisible entities and robots are not drawn, but can still be interacted with in otherwise normal ways. System robots are by default invisible. | +| `orientationMap` | `fromList []` | [orientation-map](#orientation-map "Link to object properties") | | +| `priority` | `1` | `number` | When multiple entities and robots occupy the same cell, the one with the highest priority is drawn. By default, entities have priority `1`, and robots have priority `10`. | + +### Recipe + +Recipe describes a process that takes some inputs and produces some +outputs, which robots can access using `make` and `drill`. + +| Key | Default? | Type | Description | +|------------|----------|-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `in` | | [inventory](#inventory "Link to object properties") | A list of ingredients consumed by the recipe. Each ingredient is a tuple consisting of an integer and an entity name, indicating the number of copies of the given entity that are needed. | +| `out` | | [inventory](#inventory "Link to object properties") | A list of outputs produced by the recipe. It is a list of `[count, entity name]` tuples just like `in`. | +| `required` | `[]` | [inventory](#inventory "Link to object properties") | A list of catalysts required by the recipe. They are neither consumed nor produced, but must be present in order for the recipe to be carried out. It is a list of \[count, entity name\] tuples just like in and out. | +| `time` | `1` | `number` | The number of ticks the recipe takes to perform. For recipes which take more than 1 tick, the robot will wait for a number of ticks until the recipe is complete. For example, this is used for many drilling recipes. | +| `weight` | `1` | `number` | Whenever there are multiple recipes that match the relevant criteria, one of them will be chosen at random, with probability proportional to their weights. For example, suppose there are two recipes that both output a widget, one with weight `1` and the other with weight `9`. When a robot executes `make "widget"`, the first recipe will be chosen 10% of the time, and the second recipe 90% of the time. | + +### Inventory + +List of [entity-count](#entity-count "Link to object properties") + +### Entity count + +One row in an inventory list + +| Index | Type | Description | +|-------|----------|-------------| +| `0` | `number` | Quantity | +| `1` | `string` | Entity name | + ### Waypoint Explicit waypoint definition @@ -294,16 +329,6 @@ Mapping from cardinal directions to display characters | `south` | | `string` | | | `west` | | `string` | | -### Placement - -Structure placement - -| Key | Default? | Type | Description | -|----------|----------|------------------------------------------------------------------------|------------------------------| -| `offset` | | [planar-loc](#planar-location "Link to object properties") | | -| `orient` | | [structure-orient](#structure-orientation "Link to object properties") | | -| `src` | | `string` | Name of structure definition | - ### Planar location x and y coordinates of a location in a particular world @@ -346,11 +371,12 @@ Min/max range of a value | `0` | `number` | minimum | | `1` | `number` | maximum | -### Structure orientation +### Terrain -Structure orientation properties +Description of a terrain in the Swarm game -| Key | Default? | Type | Description | -|--------|----------|-----------|-------------| -| `flip` | | `boolean` | | -| `up` | | `string` | | +| Key | Default? | Type | Description | +|---------------|----------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `attr` | | `string` | The name of the attribute that should be used to style the robot or entity. A list of currently valid attributes can be found [here](https://github.com/swarm-game/swarm/blob/main/src/Swarm/TUI/View/Attribute/Attr.hs). | +| `description` | | `string` | A description of the terrain. | +| `name` | | `string` | The name of the terrain. |