Skip to content

Generate command table #531

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 4, 2022
3 changes: 2 additions & 1 deletion app/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Data.Text.IO qualified as Text
import GitHash (giBranch, giHash, tGitInfoCwdTry)
import Options.Applicative
import Swarm.App (appMain)
import Swarm.DocGen (EditorType (..), GenerateDocs (..), generateDocs)
import Swarm.DocGen (EditorType (..), GenerateDocs (..), SheetType (..), generateDocs)
import Swarm.Language.LSP (lspMain)
import Swarm.Language.Pipeline (processTerm)
import System.Exit (exitFailure, exitSuccess)
Expand Down Expand Up @@ -44,6 +44,7 @@ cliParser =
subparser . mconcat $
[ command "recipes" (info (pure RecipeGraph) $ progDesc "Output graphviz dotfile of entity dependencies based on recipes")
, command "editors" (info (EditorKeywords <$> editor <**> helper) $ progDesc "Output editor keywords")
, command "cheatsheet" (info (pure $ CheatSheet $ Just Commands) $ progDesc "Output nice Wiki tables")
]
editor :: Parser (Maybe EditorType)
editor =
Expand Down
57 changes: 57 additions & 0 deletions src/Swarm/DocGen.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Swarm.DocGen (
generateDocs,
GenerateDocs (..),
EditorType (..),
SheetType (..),

-- ** Formatted keyword lists
keywordsCommands,
Expand All @@ -19,6 +20,7 @@ import Control.Monad.Except (ExceptT, runExceptT)
import Data.Bifunctor (Bifunctor (bimap))
import Data.Containers.ListUtils (nubOrd)
import Data.Foldable (toList)
import Data.List (transpose)
import Data.Map.Lazy (Map)
import Data.Map.Lazy qualified as Map
import Data.Maybe (fromMaybe)
Expand All @@ -34,8 +36,11 @@ import Swarm.Game.Recipe (Recipe, loadRecipes, recipeInputs, recipeOutputs, reci
import Swarm.Game.Robot (installedDevices, robotInventory, setRobotID)
import Swarm.Game.Scenario (Scenario, loadScenario, scenarioRobots)
import Swarm.Game.WorldGen (testWorld2Entites)
import Swarm.Language.Capability (capabilityName, constCaps)
import Swarm.Language.Pretty (prettyText)
import Swarm.Language.Syntax (Const (..), ConstMeta (..))
import Swarm.Language.Syntax qualified as Syntax
import Swarm.Language.Typecheck (inferConst)
import Swarm.Util (isRightOr)
import Text.Dot (Dot, NodeId, (.->.))
import Text.Dot qualified as Dot
Expand All @@ -53,11 +58,15 @@ data GenerateDocs where
RecipeGraph :: GenerateDocs
-- | Keyword lists for editors.
EditorKeywords :: Maybe EditorType -> GenerateDocs
CheatSheet :: Maybe SheetType -> GenerateDocs
deriving (Eq, Show)

data EditorType = Emacs | VSCode
deriving (Eq, Show, Enum, Bounded)

data SheetType = Entities | Commands | Capabilities | Recipes
deriving (Eq, Show, Enum, Bounded)

generateDocs :: GenerateDocs -> IO ()
generateDocs = \case
RecipeGraph -> generateRecipe >>= putStrLn
Expand All @@ -72,6 +81,11 @@ generateDocs = \case
putStrLn $ replicate 40 '-'
generateEditorKeywords et
mapM_ editorGen [minBound .. maxBound]
CheatSheet s -> case s of
Nothing -> error "Not implemented"
Just st -> case st of
Commands -> T.putStrLn commandTable
_ -> error "Not implemented"

-- ----------------------------------------------------------------------------
-- GENERATE KEYWORDS: LIST OF WORDS TO BE HIGHLIGHTED
Expand Down Expand Up @@ -134,6 +148,49 @@ operatorNames = T.intercalate "|" $ map (escape . constSyntax) (filter isOperato
ConstMBinOp {} -> True
ConstMFunc {} -> False

-- ----------------------------------------------------------------------------
-- GENERATE TABLES: COMMANDS, ENTITIES AND CAPABILITIES TO MARKDOWN TABLE
-- ----------------------------------------------------------------------------

wrap :: Char -> Text -> Text
wrap c = T.cons c . flip T.snoc c

codeQuote :: Text -> Text
codeQuote = wrap '`'

separatingLine :: [Int] -> Text
separatingLine ws = T.cons '|' . T.concat $ map (flip T.snoc '|' . flip T.replicate "-" . (2 +)) ws

listToRow :: [Int] -> [Text] -> Text
listToRow mw xs = wrap '|' . T.intercalate "|" $ zipWith format mw xs
where
format w x = wrap ' ' x <> T.replicate (w - T.length x) " "

maxWidths :: [[Text]] -> [Int]
maxWidths = map (maximum . map T.length) . transpose

-- ---------
-- COMMANDS
-- ---------

commandHeader :: [Text]
commandHeader = ["Syntax", "Type", "Capability", "Description"]

commandToList :: Const -> [Text]
commandToList c =
[ codeQuote $ constSyntax c
, codeQuote . prettyText $ inferConst c
, maybe "" capabilityName $ constCaps c
, Syntax.briefDoc . Syntax.constDoc $ Syntax.constInfo c
]

commandTable :: Text
commandTable = T.unlines $ header <> map (listToRow mw) commandRows
where
mw = maxWidths (commandHeader : commandRows)
commandRows = map commandToList Syntax.allConst
header = [listToRow mw commandHeader, separatingLine mw]

-- ----------------------------------------------------------------------------
-- GENERATE GRAPHVIZ: ENTITY DEPENDENCIES BY RECIPES
-- ----------------------------------------------------------------------------
Expand Down
221 changes: 140 additions & 81 deletions src/Swarm/Language/Syntax.hs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module Swarm.Language.Syntax (
Const (..),
allConst,
ConstInfo (..),
ConstDoc (..),
ConstMeta (..),
MBinAssoc (..),
MUnAssoc (..),
Expand Down Expand Up @@ -65,22 +66,21 @@ module Swarm.Language.Syntax (
) where

import Control.Lens (Plated (..), Traversal', (%~))
import Data.Aeson.Types
import Data.Data (Data)
import Data.Data.Lens (uniplate)
import Data.Hashable (Hashable)
import Data.Int (Int64)
import Data.Map qualified as M
import Data.Maybe (fromMaybe, isJust, mapMaybe)
import Data.Set qualified as S
import Data.String (IsString (fromString))
import Data.Text hiding (filter, map)
import Data.Text qualified as T
import Linear

import Data.Aeson.Types
import Data.Data (Data)
import Data.Hashable (Hashable)
import GHC.Generics (Generic)
import Witch.From (from)

import Data.Maybe (fromMaybe, isJust, mapMaybe)
import Linear
import Swarm.Language.Types
import Witch.From (from)

------------------------------------------------------------
-- Constants
Expand Down Expand Up @@ -196,7 +196,7 @@ data Const
-- | Do nothing. This is different than 'Wait'
-- in that it does not take up a time step.
Noop
| -- | Wait for one time step without doing anything.
| -- | Wait for a number of time steps without doing anything.
Wait
| -- | Self-destruct.
Selfdestruct
Expand Down Expand Up @@ -362,9 +362,16 @@ data ConstInfo = ConstInfo
{ syntax :: Text
, fixity :: Int
, constMeta :: ConstMeta
, constDoc :: ConstDoc
}
deriving (Eq, Ord, Show)

data ConstDoc = ConstDoc {briefDoc :: Text, longDoc :: Text}
deriving (Eq, Ord, Show)

instance IsString ConstDoc where
fromString = flip ConstDoc "" . T.pack

data ConstMeta
= -- | Function with arity of which some are commands
ConstMFunc Int Bool
Expand Down Expand Up @@ -426,79 +433,131 @@ isUserFunc c = case constMeta $ constInfo c of
-- matching gives us warning if we add more constants.
constInfo :: Const -> ConstInfo
constInfo c = case c of
Wait -> commandLow 0
Noop -> commandLow 0
Selfdestruct -> commandLow 0
Move -> commandLow 0
Turn -> commandLow 1
Grab -> commandLow 0
Harvest -> commandLow 0
Place -> commandLow 1
Give -> commandLow 2
Install -> commandLow 2
Make -> commandLow 1
Has -> commandLow 1
Count -> commandLow 1
Reprogram -> commandLow 2
Drill -> commandLow 1
Build -> commandLow 2
Salvage -> commandLow 0
Say -> commandLow 1
Log -> commandLow 1
View -> commandLow 1
Appear -> commandLow 1
Create -> commandLow 1
Whereami -> commandLow 0
Blocked -> commandLow 0
Scan -> commandLow 0
Upload -> commandLow 1
Ishere -> commandLow 1
Self -> functionLow 0
Parent -> functionLow 0
Base -> functionLow 0
Whoami -> commandLow 0
Setname -> commandLow 1
Random -> commandLow 1
Run -> commandLow 1
Return -> commandLow 1
Try -> commandLow 2
Undefined -> functionLow 0
Fail -> functionLow 1
If -> functionLow 3
Inl -> functionLow 1
Inr -> functionLow 1
Case -> functionLow 3
Fst -> functionLow 1
Snd -> functionLow 1
Force -> functionLow 1
Not -> functionLow 1
Neg -> unaryOp "-" 7 P
Add -> binaryOp "+" 6 L
And -> binaryOp "&&" 3 R
Or -> binaryOp "||" 2 R
Sub -> binaryOp "-" 6 L
Mul -> binaryOp "*" 7 L
Div -> binaryOp "/" 7 L
Exp -> binaryOp "^" 8 R
Eq -> binaryOp "==" 4 N
Neq -> binaryOp "!=" 4 N
Lt -> binaryOp "<" 4 N
Gt -> binaryOp ">" 4 N
Leq -> binaryOp "<=" 4 N
Geq -> binaryOp ">=" 4 N
Format -> functionLow 1
Concat -> binaryOp "++" 6 R
AppF -> binaryOp "$" 0 R
Teleport -> commandLow 2
As -> commandLow 2
RobotNamed -> commandLow 1
RobotNumbered -> commandLow 1
Knows -> commandLow 1
Wait -> commandLow 0 "Wait for a number of time steps."
Noop ->
commandLow 0 . doc "Do nothing." $
[ "This is different than 'Wait' in that it does not take up a time step."
, "It is useful for commands like if, which requires you to provide both branches."
, "Usually it is automatically inserted where needed, so you do not have to worry about it."
]
Selfdestruct ->
commandLow 0 . doc "Self-destruct the robot." $
[ "Useful to not clutter the world."
, "This destroys the robots inventory, so consider 'salvage' as an alternative."
]
Move -> commandLow 0 "Move forward one step."
Turn -> commandLow 1 "Turn in some direction."
Grab -> commandLow 0 "Grab an item from the current location."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Grab -> commandLow 0 "Grab an item from the current location."
Grab -> commandLow 0 "Grab an item from the current cell."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I do not think a player will find the "cell" understandable. My understanding is that "cell" is more of a game developer term than gamer slang.

Harvest ->
commandLow 0 . doc "Harvest an item from the current location." $
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
commandLow 0 . doc "Harvest an item from the current location." $
commandLow 0 . doc "Harvest an item from the current cell." $

[ "Leaves behind a growing seed if the harvested item is growable."
, "Otherwise it works exactly like 'grab'"
]
Place ->
commandLow 1 . doc "Place an item at the current location." $
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
commandLow 1 . doc "Place an item at the current location." $
commandLow 1 . doc "Place an item in the current cell." $

["The current location has to be empty for this to work."]
Give -> commandLow 2 "Give an item to another robot closeby."
Install -> commandLow 2 "Install a device from inventory on a robot."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Install -> commandLow 2 "Install a device from inventory on a robot."
Install -> commandLow 2 "Install a device from inventory on given robot reference."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think installing stuff on references makes sense.

Make -> commandLow 1 "Make an item using a recipe."
Has -> commandLow 1 "Sense whether the robot has a given item in its inventory."
Count -> commandLow 1 "Get the count of a given item in robots inventory."
Reprogram ->
commandLow 2 . doc "Reprogram another robot with new command." $
["The other robot has to be closeby and idle."]
Drill ->
commandLow 1 . doc "Drill through an entity." $
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
commandLow 1 . doc "Drill through an entity." $
commandLow 1 . doc "Drill through a drillable entity." $

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I am content leaving that discovery to the player. 😄

Anyway, if you wanted to include some specifics, I would suggest doing it in the longer description.

[ "Usually you want to 'drill forward' when exploring to clear out obstacles."
, "When you have found a source to drill, you can stand on it and 'drill down'."
, "See what recipes with drill you have available."
]
Build ->
commandLow 1 . doc "Construct a new robot." $
[ "You can specify a command for the robot to execute."
, "If the command requires a device it will be installed from your inventory."
]
Salvage ->
commandLow 0 . doc "Deconstruct an old robot." $
["Salvaging a robot will give you its inventory, installed devices and log."]
Say ->
commandLow 1 . doc "Emit a message." $ -- TODO: #513
[ "The message will be in global log, which you can not currently view."
, "https://github.com/swarm-game/swarm/issues/513"
]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log -> commandLow 1 "Log the string in the robots logger."
View -> commandLow 1 "View the given robot."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
View -> commandLow 1 "View the given robot."
View -> commandLow 1 "View the given robot reference."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it does not make sense to view a reference to me, unless you want to see:

<r1> : robot

Appear ->
commandLow 1 . doc "Set how the robot is displayed." $
[ "You can either specify one character or five (for each direction)."
, "The default is \"X^>v<\"."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the 'X' ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the robot is looking down. So it looks like four robots meet.

Anyway, I would like to make that customizable one day, so that powerline font users can choose prettier versions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand, but how would the player ever be able to see that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean build {move; turn down}?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*gape* Wow, I never knew that a robot cold turn downwards!

]
Create ->
commandLow 1 . doc "Create an item out of thin air." $
["Only available in creative mode."]
Whereami -> commandLow 0 "Get the current x, y coordinates."
Blocked -> commandLow 0 "See if the robot can move forward."
Scan ->
commandLow 0 . doc "Scan a nearby location for entities." $
[ "Adds the entity (not robot) to your inventory with count 0 if there is any."
, "If you can use sum types, you can also inspect the result directly."
]
Upload -> commandLow 1 "Upload robots known entities to another robot."
Ishere -> commandLow 1 "See if a specific entity is in current location."
Self -> functionLow 0 "Get a reference to current robot."
Parent -> functionLow 0 "Get a reference to the robot's parent."
Base -> functionLow 0 "Get a reference to the base robot."
Whoami -> commandLow 0 "Get the robot's display name."
Setname -> commandLow 1 "Set the robot's display name."
Random ->
commandLow 1 . doc "Get a uniformly random integer." $
["The random integer will be from the range 0 to END inclusive of your choice."]
Run -> commandLow 1 "Run a program loaded from a file."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Run -> commandLow 1 "Run a program loaded from a file."
Run -> commandLow 1 "Run a program from a file."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not too clear on the semantics here, but loading the program from a file sounds like English to me. 🤨

Return -> commandLow 1 "Make the value a result in 'cmd'."
Try -> commandLow 2 "Execute a command, catching errors."
Undefined -> functionLow 0 "A value of any type, that is evaluated as error."
Fail -> functionLow 1 "A value of any type, that is evaluated as error with message."
If ->
functionLow 3 . doc "If-Then-Else function." $
["If the bool predicate is true then evaluate the first expression, otherwise the second."]
Inl -> functionLow 1 "Put the value into the left component of a sum type."
Inr -> functionLow 1 "Put the value into the right component of a sum type."
Case -> functionLow 3 "Evaluate one of the given functions on a value of sum type."
Fst -> functionLow 1 "Get the first value of a pair."
Snd -> functionLow 1 "Get the second value of a pair."
Force -> functionLow 1 "Force the evaluation of a delayed evaluation value."
Not -> functionLow 1 "Negate the boolean value."
Neg -> unaryOp "-" 7 P "Subtract the given integer value."
Add -> binaryOp "+" 6 L "Add the given integer values."
And -> binaryOp "&&" 3 R "Logical and (true if both values are true)."
Or -> binaryOp "||" 2 R "Logical or (true if either value is true)."
Sub -> binaryOp "-" 6 L "Add the given integer values."
Mul -> binaryOp "*" 7 L "Multiply the given integer values."
Div -> binaryOp "/" 7 L "Divide the left integer value by the right one."
Exp -> binaryOp "^" 8 R "Raise the left integer value to the power of the right one."
Eq -> binaryOp "==" 4 N "Check that the left value is equal to the right one."
Neq -> binaryOp "!=" 4 N "Check that the left value is not equal to the right one."
Lt -> binaryOp "<" 4 N "Check that the left value is lesser than the right one."
Gt -> binaryOp ">" 4 N "Check that the left value is greater than the right one."
Leq -> binaryOp "<=" 4 N "Check that the left value is lesser or equal to the right one."
Geq -> binaryOp ">=" 4 N "Check that the left value is greater or equal to the right one."
Format -> functionLow 1 "Turn an arbitrary value into a string."
Concat -> binaryOp "++" 6 R "Concatenate the given strings."
AppF ->
binaryOp "$" 0 R . doc "Apply the function on the left to the value on the right." $
[ "This operator is useful to avoid nesting parentheses."
, "For exaple:"
, "'f $ g $ h x = f (g (h x))'"
]
Teleport -> commandLow 2 "Teleport a robot to the given location."
As -> commandLow 2 "Hypothetically run a command as if you were another robot."
RobotNamed -> commandLow 1 "Find a robot by name."
RobotNumbered -> commandLow 1 "Find a robot by number."
Knows -> commandLow 1 "Check if the robot knows an entity."
where
unaryOp s p side = ConstInfo {syntax = s, fixity = p, constMeta = ConstMUnOp side}
binaryOp s p side = ConstInfo {syntax = s, fixity = p, constMeta = ConstMBinOp side}
command s a = ConstInfo {syntax = s, fixity = 11, constMeta = ConstMFunc a True}
function s a = ConstInfo {syntax = s, fixity = 11, constMeta = ConstMFunc a False}
doc b ls = ConstDoc b (T.unlines ls)
unaryOp s p side d = ConstInfo {syntax = s, fixity = p, constMeta = ConstMUnOp side, constDoc = d}
binaryOp s p side d = ConstInfo {syntax = s, fixity = p, constMeta = ConstMBinOp side, constDoc = d}
command s a d = ConstInfo {syntax = s, fixity = 11, constMeta = ConstMFunc a True, constDoc = d}
function s a d = ConstInfo {syntax = s, fixity = 11, constMeta = ConstMFunc a False, constDoc = d}
-- takes the number of arguments for a commmand
commandLow = command (lowShow c)
functionLow = function (lowShow c)
Expand Down
Loading