I’m still a bigger fan of an “official” language server provider (LSP), see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/. This would seem more doable, since an LSP is just another REST API…so the LSP could just be part of REST API with different root URL. Since the existing /console/inspect largely provides some/most of the data needed (i.e. request=completion, request=highlight and request=syntax), it mainly be just re-packaging that data in the JSON-RPC used by LSP via RouterOS web server.
And with an RouterOS LSP every mainstream editor (like VSCode with CoPilot) have an authoritative source specific to [a] RouterOS device to accurately do completion and validate syntax, including extra features and be version-specific. And certainly CoPilot (or similar LLM connection to editor) does/should/cloud use the LSP to augment the LLM. e.g. not researched, but imagine some MCP that linked to an installed LSP exists.
Not saying BNF would not be NICE. Just not sure some text with BNF notation for scripting ALONE get you much, since it’s still needed incorporate in some TDB AI workflow to provide any value. The secondary problem is the BNF actually changes depending on version and if you have extra-packages. (Which is why RouterOS supporting being an LSP server be more useful, since it knows the entire AST for that device, and mostly available via /console/inspect data today)
While an LLM isn’t good at scripting. An LLM can generate BNF… so if BNF useful, an LLM could generated one… So for curiosity, just provided the free Claude LLM the MikroTik scripting doc as PDF and get some starting place in under 5 minutes. I suspect if used paid model, and/or iterated prompting a few more times – like providing the “Examples” and “Tips and Tricks” in docs – it like be even more refined. And certainly if MikroTIk did similar providing the theorized LEX/YACC (or whatever code implements scripting/CLI), I’d imagine it be MUCH closer… But just the PDF of scripting manual got me this, far from perfect but I suspect spending a few hours on it & spending money, an LLM would get pretty close to BNF for RouterOS.
But question be if the following BNF was 100% perfect (which it’s NOT), what does that get us*? (*without more work beyond the BNF)
# MikroTik RouterOS Scripting Language BNF
## General Structure
::= *
::= [] [] [] [] []
| "[" "]"
::= ":" | "/"
::= ["/" ]*
::=
::=
::= []+
::= "="
::= ";" | NEWLINE
```
## Expressions
```
::=
|
|
|
|
|
|
::=
|
|
|
|
|
|
|
|
|
|
::= ["-"] + ["." +]
| "0x" +
::= "\"" * "\""
::=
|
::= "\\" | "\"" | "\n" | "\r" | "\t" | "\$" | "\?" | "\_"
| "\a" | "\b" | "\f" | "\v" | "\xx"
::= "true" | "false"
::= "." "." "."
::= | | "1" | "2" | "25" ["0"-"5"]
::= "/"
::= | | "3" ["0"-"2"]
::=
::= "/"
::= | | "1" | "12"
::= "*" +
::=
::= "{" [ [";" ]*] "}"
::=
| "="
::= "nil"
::= "$"
| "$" "(" ")"
| "$" "[" "]"
::= "-"
| "!"
| "~"
::=
::= "+" | "-" | "*" | "/" | "%"
| "<" | ">" | "=" | "<=" | ">=" | "!="
| "&&" | "and" | "||" | "or" | "in"
| "|" | "^" | "&" | "<>"
| "." | "," | "->"
::= "(" ")"
::= "[" "]"
::= "->"
```
## Control Structures
```
::=
|
|
|
|
::= ":if" "(" ")" "do=" "{" * "}" ["else=" "{" * "}"]
::= ":do" "{" * "}" "while=" "(" ")"
::= ":while" "(" ")" "do=" "{" * "}"
::= ":for" "from=" "to=" ["step=" ] "do=" "{" * "}"
::= ":foreach" ["," ] "in=" "do=" "{" * "}"
```
## Scopes and Variable Declarations
```
::= "{" * "}"
::=
|
::= ":global" []
::= ":local" []
```
## Commands
```
::= ":set" []
::= ":put"
::= ":log"
::= "debug" | "error" | "info" | "warning"
::= ":delay"
::= ":error"
::= ":onerror" "in=" "{" * "}" "do=" "{" * "}"
::= ":retry" "command=" ["delay=" ] ["max=" ]
::= ":return" []
```
## Common Terminal Symbols
```
::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
::= | "A" | "B" | "C" | "D" | "E" | "F" | "a" | "b" | "c" | "d" | "e" | "f"
::= [ | ]*
| "\"" "\""
::= "A" | "B" | ... | "Z" | "a" | "b" | ... | "z"
::= ["s" | "m" | "h" | "d" | "w"]
```
## Function Definition (Workaround)
```
::= ":global" "do=" "{" * "}"
::= "$" []
```
## Comments
```
::= "#" * NEWLINE
```
## Menu Commands
```
::=
|
|
|
|
|
|
|
|
|
::= "add"
::= "remove"
::= "enable"
::= "disable"
::= "set"
::= "get"
::= "print" []*
::= "as-value"
| "brief"
| "detail"
| "count-only"
| "file"
| "follow"
| "follow-only"
| "from="
| "interval="
| "terse"
| "value-list"
| "without-paging"
| "where"
::= "export" ["file=" ]
::= "edit"
::= "find"
```
## Type Conversion Functions
```
::= ":toarray"
| ":tobool"
| ":toid"
| ":toip"
| ":toip6"
| ":tonum"
| ":tostr"
| ":totime"
| ":tonsec"
| ":tocrlf"
| ":tolf"
```
## Utility Functions
```
::= ":len"
| ":typeof"
| ":pick" []
| ":find" []
| ":time"
| ":timestamp"
| ":resolve"
| ":execute"
| ":parse"
| ":environment" "print"
| ":nothing"
| ":convert"
| ":serialize"
| ":deserialize"
| ":range"
| ":rndnum" "from=" "to="
| ":rndstr" ["from=" ] ["length=" ]
| ":beep" ["frequency=" ] ["length=" ]
::=
| "domain-name=" ["server=" ] ["port=" ] ["type=" ]
::= "any" | "any6" | "ipv4" | "ipv6"
::= "to=" ["transform=" ]
::= "base32" | "base64" | "bit-array-lsb" | "bit-array-msb" | "byte-array" | "hex" | "num" | "raw" | "url"
::= "lc" | "uc" | "lcfirst" | "ucfirst" | "crlf" | "ed25519-private-to-x25519-private" | "none" | "rot13" | "x25519-private-to-x25519-public" | "ed25519-private-to-ed25519-public" | "ed25519-public-to-x25519-public" | "md5" | "reverse" | "sha512"
::= ["value=" ] ["to=" ] []
::= "json" | "dsv"
::= ["delimiter=" ] ["order=" ] ["options=" ] ["file-name=" ]
::= ["," ]*
::= "json.pretty" | "json.no-string-conversion" | "dsv.wrap-strings" | "dsv.ignore-size" | "dsv.remap"
::= ["value=" ] ["from=" ] []
::= "json" | "dsv"
::= ["delimiter=" ] ["options=" ]
```
```