Skip to content

Commit f2ad132

Browse files
authored
Generate command table (#531)
Generate table for [Commands Cheat Sheet](https://github.com/swarm-game/swarm/wiki/Commands-Cheat-Sheet) Wiki. - part of #344
1 parent 45755c4 commit f2ad132

File tree

8 files changed

+303
-123
lines changed

8 files changed

+303
-123
lines changed

app/Main.hs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Data.Text.IO qualified as Text
99
import GitHash (giBranch, giHash, tGitInfoCwdTry)
1010
import Options.Applicative
1111
import Swarm.App (appMain)
12-
import Swarm.DocGen (EditorType (..), GenerateDocs (..), generateDocs)
12+
import Swarm.DocGen (EditorType (..), GenerateDocs (..), SheetType (..), generateDocs)
1313
import Swarm.Language.LSP (lspMain)
1414
import Swarm.Language.Pipeline (processTerm)
1515
import System.Exit (exitFailure, exitSuccess)
@@ -44,6 +44,7 @@ cliParser =
4444
subparser . mconcat $
4545
[ command "recipes" (info (pure RecipeGraph) $ progDesc "Output graphviz dotfile of entity dependencies based on recipes")
4646
, command "editors" (info (EditorKeywords <$> editor <**> helper) $ progDesc "Output editor keywords")
47+
, command "cheatsheet" (info (pure $ CheatSheet $ Just Commands) $ progDesc "Output nice Wiki tables")
4748
]
4849
editor :: Parser (Maybe EditorType)
4950
editor =

editors/emacs/swarm-mode.el

+14-14
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,20 @@
2929
;; cabal run swarm:swarm -- generate editors --emacs
3030
(x-keywords '("def" "end"))
3131
(x-builtins '(
32+
"self"
33+
"parent"
34+
"base"
3235
"if"
33-
"run"
34-
"return"
35-
"try"
36-
"fail"
37-
"force"
36+
"inl"
37+
"inr"
38+
"case"
3839
"fst"
3940
"snd"
41+
"force"
42+
"undefined"
43+
"fail"
44+
"not"
45+
"format"
4046
))
4147
(x-commands '(
4248
"noop"
@@ -66,18 +72,12 @@
6672
"scan"
6773
"upload"
6874
"ishere"
69-
"self"
70-
"parent"
71-
"base"
7275
"whoami"
7376
"setname"
7477
"random"
75-
"inl"
76-
"inr"
77-
"case"
78-
"undefined"
79-
"not"
80-
"format"
78+
"run"
79+
"return"
80+
"try"
8181
"teleport"
8282
"as"
8383
"robotnamed"

editors/vscode/syntaxes/swarm.tmLanguage.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
},
5757
{
5858
"name": "keyword.other",
59-
"match": "\\b(?i)(noop|wait|selfdestruct|move|turn|grab|harvest|place|give|install|make|has|count|drill|build|salvage|reprogram|say|log|view|appear|create|whereami|blocked|scan|upload|ishere|self|parent|base|whoami|setname|random|run|if|inl|inr|case|fst|snd|force|return|try|undefined|fail|not|format|teleport|as|robotnamed|robotnumbered|knows)\\b"
59+
"match": "\\b(?i)(self|parent|base|if|inl|inr|case|fst|snd|force|undefined|fail|not|format|noop|wait|selfdestruct|move|turn|grab|harvest|place|give|install|make|has|count|drill|build|salvage|reprogram|say|log|view|appear|create|whereami|blocked|scan|upload|ishere|whoami|setname|random|run|return|try|teleport|as|robotnamed|robotnumbered|knows)\\b"
6060
}
6161
]
6262
},

src/Swarm/DocGen.hs

+111-16
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@ module Swarm.DocGen (
44
generateDocs,
55
GenerateDocs (..),
66
EditorType (..),
7+
SheetType (..),
78

89
-- ** Formatted keyword lists
910
keywordsCommands,
1011
keywordsDirections,
1112
operatorNames,
12-
builtinCommandsListEmacs,
13+
builtinFunctionList,
1314
editorList,
15+
16+
-- ** Wiki pages
17+
commandsPage,
1418
) where
1519

1620
import Control.Lens (view, (^.))
@@ -19,6 +23,7 @@ import Control.Monad.Except (ExceptT, runExceptT)
1923
import Data.Bifunctor (Bifunctor (bimap))
2024
import Data.Containers.ListUtils (nubOrd)
2125
import Data.Foldable (toList)
26+
import Data.List (transpose)
2227
import Data.Map.Lazy (Map)
2328
import Data.Map.Lazy qualified as Map
2429
import Data.Maybe (fromMaybe)
@@ -34,8 +39,11 @@ import Swarm.Game.Recipe (Recipe, loadRecipes, recipeInputs, recipeOutputs, reci
3439
import Swarm.Game.Robot (installedDevices, robotInventory, setRobotID)
3540
import Swarm.Game.Scenario (Scenario, loadScenario, scenarioRobots)
3641
import Swarm.Game.WorldGen (testWorld2Entites)
37-
import Swarm.Language.Syntax (Const (..), ConstMeta (..))
42+
import Swarm.Language.Capability (capabilityName, constCaps)
43+
import Swarm.Language.Pretty (prettyText)
44+
import Swarm.Language.Syntax (Const (..))
3845
import Swarm.Language.Syntax qualified as Syntax
46+
import Swarm.Language.Typecheck (inferConst)
3947
import Swarm.Util (isRightOr)
4048
import Text.Dot (Dot, NodeId, (.->.))
4149
import Text.Dot qualified as Dot
@@ -53,11 +61,15 @@ data GenerateDocs where
5361
RecipeGraph :: GenerateDocs
5462
-- | Keyword lists for editors.
5563
EditorKeywords :: Maybe EditorType -> GenerateDocs
64+
CheatSheet :: Maybe SheetType -> GenerateDocs
5665
deriving (Eq, Show)
5766

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

70+
data SheetType = Entities | Commands | Capabilities | Recipes
71+
deriving (Eq, Show, Enum, Bounded)
72+
6173
generateDocs :: GenerateDocs -> IO ()
6274
generateDocs = \case
6375
RecipeGraph -> generateRecipe >>= putStrLn
@@ -72,6 +84,11 @@ generateDocs = \case
7284
putStrLn $ replicate 40 '-'
7385
generateEditorKeywords et
7486
mapM_ editorGen [minBound .. maxBound]
87+
CheatSheet s -> case s of
88+
Nothing -> error "Not implemented"
89+
Just st -> case st of
90+
Commands -> T.putStrLn commandsPage
91+
_ -> error "Not implemented"
7592

7693
-- ----------------------------------------------------------------------------
7794
-- GENERATE KEYWORDS: LIST OF WORDS TO BE HIGHLIGHTED
@@ -81,24 +98,30 @@ generateEditorKeywords :: EditorType -> IO ()
8198
generateEditorKeywords = \case
8299
Emacs -> do
83100
putStrLn "(x-builtins '("
84-
T.putStr . editorList Emacs $ map constSyntax builtinCommandsEmacs
101+
T.putStr $ builtinFunctionList Emacs
85102
putStrLn "))\n(x-commands '("
86103
T.putStr $ keywordsCommands Emacs
87104
T.putStr $ keywordsDirections Emacs
88105
putStrLn "))"
89106
VSCode -> do
90107
putStrLn "Functions and commands:"
91-
T.putStrLn $ keywordsCommands VSCode
108+
T.putStrLn $ builtinFunctionList VSCode <> "|" <> keywordsCommands VSCode
92109
putStrLn "\nDirections:"
93110
T.putStrLn $ keywordsDirections VSCode
94111
putStrLn "\nOperators:"
95112
T.putStrLn operatorNames
96113

97-
builtinCommandsEmacs :: [Const]
98-
builtinCommandsEmacs = [If, Run, Return, Try, Fail, Force, Fst, Snd]
114+
commands :: [Const]
115+
commands = filter Syntax.isCmd Syntax.allConst
99116

100-
builtinCommandsListEmacs :: Text
101-
builtinCommandsListEmacs = editorList Emacs $ map constSyntax builtinCommandsEmacs
117+
operators :: [Const]
118+
operators = filter Syntax.isOperator Syntax.allConst
119+
120+
builtinFunctions :: [Const]
121+
builtinFunctions = filter Syntax.isBuiltinFunction Syntax.allConst
122+
123+
builtinFunctionList :: EditorType -> Text
124+
builtinFunctionList e = editorList e $ map constSyntax builtinFunctions
102125

103126
editorList :: EditorType -> [Text] -> Text
104127
editorList = \case
@@ -112,27 +135,99 @@ constSyntax = Syntax.syntax . Syntax.constInfo
112135

113136
-- | Get formatted list of basic functions/commands.
114137
keywordsCommands :: EditorType -> Text
115-
keywordsCommands e = editorList e $ map constSyntax (filter isFunc Syntax.allConst)
116-
where
117-
isFunc c = Syntax.isUserFunc c && (e /= Emacs || c `notElem` builtinCommandsEmacs)
138+
keywordsCommands e = editorList e $ map constSyntax commands
118139

119140
-- | Get formatted list of directions.
120141
keywordsDirections :: EditorType -> Text
121142
keywordsDirections e = editorList e $ map (Syntax.dirSyntax . Syntax.dirInfo) Syntax.allDirs
122143

123144
operatorNames :: Text
124-
operatorNames = T.intercalate "|" $ map (escape . constSyntax) (filter isOperator Syntax.allConst)
145+
operatorNames = T.intercalate "|" $ map (escape . constSyntax) operators
125146
where
126147
special :: String
127148
special = "*+$[]|^"
128149
slashNotComment = \case
129150
'/' -> "/(?![/|*])"
130151
c -> T.singleton c
131152
escape = T.concatMap (\c -> if c `elem` special then T.snoc "\\\\" c else slashNotComment c)
132-
isOperator c = case Syntax.constMeta $ Syntax.constInfo c of
133-
ConstMUnOp {} -> True
134-
ConstMBinOp {} -> True
135-
ConstMFunc {} -> False
153+
154+
-- ----------------------------------------------------------------------------
155+
-- GENERATE TABLES: COMMANDS, ENTITIES AND CAPABILITIES TO MARKDOWN TABLE
156+
-- ----------------------------------------------------------------------------
157+
158+
wrap :: Char -> Text -> Text
159+
wrap c = T.cons c . flip T.snoc c
160+
161+
codeQuote :: Text -> Text
162+
codeQuote = wrap '`'
163+
164+
escapeTable :: Text -> Text
165+
escapeTable = T.concatMap (\c -> if c == '|' then T.snoc "\\" c else T.singleton c)
166+
167+
separatingLine :: [Int] -> Text
168+
separatingLine ws = T.cons '|' . T.concat $ map (flip T.snoc '|' . flip T.replicate "-" . (2 +)) ws
169+
170+
listToRow :: [Int] -> [Text] -> Text
171+
listToRow mw xs = wrap '|' . T.intercalate "|" $ zipWith format mw xs
172+
where
173+
format w x = wrap ' ' x <> T.replicate (w - T.length x) " "
174+
175+
maxWidths :: [[Text]] -> [Int]
176+
maxWidths = map (maximum . map T.length) . transpose
177+
178+
-- ---------
179+
-- COMMANDS
180+
-- ---------
181+
182+
commandHeader :: [Text]
183+
commandHeader = ["Syntax", "Type", "Capability", "Description"]
184+
185+
commandToList :: Const -> [Text]
186+
commandToList c =
187+
map
188+
escapeTable
189+
[ addLink (T.pack $ "#" <> show c) . codeQuote $ constSyntax c
190+
, codeQuote . prettyText $ inferConst c
191+
, maybe "" capabilityName $ constCaps c
192+
, Syntax.briefDoc . Syntax.constDoc $ Syntax.constInfo c
193+
]
194+
where
195+
addLink l t = T.concat ["[", t, "](", l, ")"]
196+
197+
constTable :: [Const] -> Text
198+
constTable cs = T.unlines $ header <> map (listToRow mw) commandRows
199+
where
200+
mw = maxWidths (commandHeader : commandRows)
201+
commandRows = map commandToList cs
202+
header = [listToRow mw commandHeader, separatingLine mw]
203+
204+
commandToSection :: Const -> Text
205+
commandToSection c =
206+
T.unlines $
207+
[ "## " <> T.pack (show c)
208+
, ""
209+
, "- syntax: " <> codeQuote (constSyntax c)
210+
, "- type: " <> (codeQuote . prettyText $ inferConst c)
211+
, maybe "" (("- required capabilities: " <>) . capabilityName) $ constCaps c
212+
, ""
213+
, Syntax.briefDoc . Syntax.constDoc $ Syntax.constInfo c
214+
]
215+
<> let l = Syntax.longDoc . Syntax.constDoc $ Syntax.constInfo c
216+
in if T.null l then [] else ["", l]
217+
218+
commandsPage :: Text
219+
commandsPage =
220+
T.intercalate "\n\n" $
221+
[ "# Commands"
222+
, constTable commands
223+
, "# Builtin functions"
224+
, "These functions are evaluated immediately once they have enough arguments."
225+
, constTable builtinFunctions
226+
, "# Operators"
227+
, constTable operators
228+
, "# Detailed descriptions"
229+
]
230+
<> map commandToSection (commands <> builtinFunctions <> operators)
136231

137232
-- ----------------------------------------------------------------------------
138233
-- GENERATE GRAPHVIZ: ENTITY DEPENDENCIES BY RECIPES

0 commit comments

Comments
 (0)