Commit022b7cb5Recorded27 Apr 2026Repositorysigil-irc
Add (sigil irc tags) and (sigil irc numerics)
Message
(sigil irc tags) — typed accessors for the well-known IRCv3 tags that show up across Tier 1 + Tier 2 capabilities:
msg-time server-time tag value (RFC 3339 UTC string)
msg-msgid msgid tag value
msg-account account tag value (or "*" for unauthenticated)
msg-batch batch reftag
msg-label labeled-response correlation id
msg-reply +draft/reply target msgid
msg-typing +typing state (active / paused / done) with-time, with-msgid, with-account, with-batch, with-label —
builders that add or replace a tag entry on an alist.(sigil irc numerics) — named bindings for ~50 numeric reply codes covering registration (001-005), channel info (3xx), errors (4xx), MOTD (37x), SASL (90x), MONITOR (73x), and the IRCv3 standard-replies command names (FAIL/WARN/NOTE).
irc-numeric-name "001" -> 'RPL-WELCOME
irc-numeric->symbol "001" -> '|001| (parser symbol form)The string form is what serializers emit; the symbol form is what (irc-message-command msg) returns after parsing.
Changed
src/sigil/irc/numerics.sgl | 353 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/sigil/irc/tags.sgl | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
test/test-numerics.sgl | 57 +++++++++++++++++++++++++++
3 files changed, 547 insertions(+)Diff
src/sigil/irc/numerics.sgladded
@@ -0,0 +1,353 @@
+1
;;; (sigil irc numerics) - IRC numeric reply constants+2
;;;+3
;;; Numeric replies as named constants. Servers send these as 3-digit+4
;;; commands (e.g. `001`, `433`, `903`). Sigil's parser uppercases the+5
;;; command and converts it to a symbol; here we expose both forms:+6
;;;+7
;;; `RPL-WELCOME` => "001" (string)+8
;;; `IRC/RPL-WELCOME` => '|001| (symbol, what the parser returns)+9
;;;+10
;;; The string form is what you serialize with `make-irc-command`. The+11
;;; symbol form is what you compare against `irc-message-command`.+12
;;;+13
;;; Categories:+14
;;; 001-005 registration / welcome / ISUPPORT+15
;;; 200-399 responses (RPL_)+16
;;; 400-599 errors (ERR_)+17
;;; 600-799 IRCv3 / extension space (SASL, MONITOR, CHATHISTORY, etc.)+18
;;;+19
;;; Source: RFC 2812 + IRCv3 specifications + de-facto registry at+20
;;; https://defs.ircdocs.horse/defs/numerics.html+21
+22
(define-library (sigil irc numerics)+23
(import (sigil core))+24
+25
(export+26
;; Lookup helpers+27
irc-numeric-name+28
irc-numeric->symbol+29
+30
;; Welcome / registration / ISUPPORT+31
RPL-WELCOME ; 001+32
RPL-YOURHOST ; 002+33
RPL-CREATED ; 003+34
RPL-MYINFO ; 004+35
RPL-ISUPPORT ; 005+36
+37
;; Server status+38
RPL-LUSERCLIENT ; 251+39
RPL-LUSEROP ; 252+40
RPL-LUSERUNKNOWN ; 253+41
RPL-LUSERCHANNELS ; 254+42
RPL-LUSERME ; 255+43
RPL-ADMINME ; 256+44
RPL-ADMINLOC1 ; 257+45
RPL-ADMINLOC2 ; 258+46
RPL-ADMINEMAIL ; 259+47
+48
;; AWAY+49
RPL-AWAY ; 301+50
RPL-USERHOST ; 302+51
RPL-ISON ; 303+52
RPL-UNAWAY ; 305+53
RPL-NOWAWAY ; 306+54
+55
;; WHOIS / WHOWAS+56
RPL-WHOISUSER ; 311+57
RPL-WHOISSERVER ; 312+58
RPL-WHOISOPERATOR ; 313+59
RPL-WHOWASUSER ; 314+60
RPL-ENDOFWHO ; 315+61
RPL-WHOISIDLE ; 317+62
RPL-ENDOFWHOIS ; 318+63
RPL-WHOISCHANNELS ; 319+64
RPL-WHOISACCOUNT ; 330+65
+66
;; Channel info+67
RPL-LIST ; 322+68
RPL-LISTEND ; 323+69
RPL-CHANNELMODEIS ; 324+70
RPL-NOTOPIC ; 331+71
RPL-TOPIC ; 332+72
RPL-TOPICWHOTIME ; 333+73
RPL-INVITING ; 341+74
RPL-INVITELIST ; 346+75
RPL-ENDOFINVITELIST ; 347+76
RPL-EXCEPTLIST ; 348+77
RPL-ENDOFEXCEPTLIST ; 349+78
RPL-WHOREPLY ; 352+79
RPL-NAMREPLY ; 353+80
RPL-ENDOFNAMES ; 366+81
+82
;; BAN+83
RPL-BANLIST ; 367+84
RPL-ENDOFBANLIST ; 368+85
+86
;; MOTD+87
RPL-MOTDSTART ; 375+88
RPL-MOTD ; 372+89
RPL-ENDOFMOTD ; 376+90
+91
;; Error responses+92
ERR-NOSUCHNICK ; 401+93
ERR-NOSUCHSERVER ; 402+94
ERR-NOSUCHCHANNEL ; 403+95
ERR-CANNOTSENDTOCHAN ; 404+96
ERR-TOOMANYCHANNELS ; 405+97
ERR-WASNOSUCHNICK ; 406+98
ERR-NORECIPIENT ; 411+99
ERR-NOTEXTTOSEND ; 412+100
ERR-INPUTTOOLONG ; 417+101
ERR-UNKNOWNCOMMAND ; 421+102
ERR-NOMOTD ; 422+103
ERR-NOADMININFO ; 423+104
ERR-NONICKNAMEGIVEN ; 431+105
ERR-ERRONEUSNICKNAME ; 432+106
ERR-NICKNAMEINUSE ; 433+107
ERR-NICKCOLLISION ; 436+108
ERR-USERNOTINCHANNEL ; 441+109
ERR-NOTONCHANNEL ; 442+110
ERR-USERONCHANNEL ; 443+111
ERR-NOLOGIN ; 444+112
ERR-NOTREGISTERED ; 451+113
ERR-NEEDMOREPARAMS ; 461+114
ERR-ALREADYREGISTERED ; 462+115
ERR-PASSWDMISMATCH ; 464+116
ERR-YOUREBANNEDCREEP ; 465+117
ERR-CHANNELISFULL ; 471+118
ERR-UNKNOWNMODE ; 472+119
ERR-INVITEONLYCHAN ; 473+120
ERR-BANNEDFROMCHAN ; 474+121
ERR-BADCHANNELKEY ; 475+122
ERR-BADCHANMASK ; 476+123
ERR-NOCHANMODES ; 477+124
ERR-CHANOPRIVSNEEDED ; 482+125
ERR-CANTKILLSERVER ; 483+126
ERR-NOOPERHOST ; 491+127
ERR-UMODEUNKNOWNFLAG ; 501+128
ERR-USERSDONTMATCH ; 502+129
+130
;; SASL (IRCv3)+131
RPL-LOGGEDIN ; 900+132
RPL-LOGGEDOUT ; 901+133
ERR-NICKLOCKED ; 902+134
RPL-SASLSUCCESS ; 903+135
ERR-SASLFAIL ; 904+136
ERR-SASLTOOLONG ; 905+137
ERR-SASLABORTED ; 906+138
ERR-SASLALREADY ; 907+139
RPL-SASLMECHS ; 908+140
+141
;; MONITOR (IRCv3)+142
RPL-MONONLINE ; 730+143
RPL-MONOFFLINE ; 731+144
RPL-MONLIST ; 732+145
RPL-ENDOFMONLIST ; 733+146
ERR-MONLISTFULL ; 734+147
+148
;; STARTTLS+149
RPL-STARTTLS ; 670+150
ERR-STARTTLS ; 691+151
+152
;; Generic error context (IRCv3 standard replies)+153
;; (Strings — these are not numeric but used as commands)+154
CMD-FAIL+155
CMD-WARN+156
CMD-NOTE)+157
+158
(begin+159
+160
(define RPL-WELCOME "001")+161
(define RPL-YOURHOST "002")+162
(define RPL-CREATED "003")+163
(define RPL-MYINFO "004")+164
(define RPL-ISUPPORT "005")+165
+166
(define RPL-LUSERCLIENT "251")+167
(define RPL-LUSEROP "252")+168
(define RPL-LUSERUNKNOWN "253")+169
(define RPL-LUSERCHANNELS "254")+170
(define RPL-LUSERME "255")+171
(define RPL-ADMINME "256")+172
(define RPL-ADMINLOC1 "257")+173
(define RPL-ADMINLOC2 "258")+174
(define RPL-ADMINEMAIL "259")+175
+176
(define RPL-AWAY "301")+177
(define RPL-USERHOST "302")+178
(define RPL-ISON "303")+179
(define RPL-UNAWAY "305")+180
(define RPL-NOWAWAY "306")+181
+182
(define RPL-WHOISUSER "311")+183
(define RPL-WHOISSERVER "312")+184
(define RPL-WHOISOPERATOR "313")+185
(define RPL-WHOWASUSER "314")+186
(define RPL-ENDOFWHO "315")+187
(define RPL-WHOISIDLE "317")+188
(define RPL-ENDOFWHOIS "318")+189
(define RPL-WHOISCHANNELS "319")+190
(define RPL-WHOISACCOUNT "330")+191
+192
(define RPL-LIST "322")+193
(define RPL-LISTEND "323")+194
(define RPL-CHANNELMODEIS "324")+195
(define RPL-NOTOPIC "331")+196
(define RPL-TOPIC "332")+197
(define RPL-TOPICWHOTIME "333")+198
(define RPL-INVITING "341")+199
(define RPL-INVITELIST "346")+200
(define RPL-ENDOFINVITELIST "347")+201
(define RPL-EXCEPTLIST "348")+202
(define RPL-ENDOFEXCEPTLIST "349")+203
(define RPL-WHOREPLY "352")+204
(define RPL-NAMREPLY "353")+205
(define RPL-ENDOFNAMES "366")+206
+207
(define RPL-BANLIST "367")+208
(define RPL-ENDOFBANLIST "368")+209
+210
(define RPL-MOTDSTART "375")+211
(define RPL-MOTD "372")+212
(define RPL-ENDOFMOTD "376")+213
+214
(define ERR-NOSUCHNICK "401")+215
(define ERR-NOSUCHSERVER "402")+216
(define ERR-NOSUCHCHANNEL "403")+217
(define ERR-CANNOTSENDTOCHAN "404")+218
(define ERR-TOOMANYCHANNELS "405")+219
(define ERR-WASNOSUCHNICK "406")+220
(define ERR-NORECIPIENT "411")+221
(define ERR-NOTEXTTOSEND "412")+222
(define ERR-INPUTTOOLONG "417")+223
(define ERR-UNKNOWNCOMMAND "421")+224
(define ERR-NOMOTD "422")+225
(define ERR-NOADMININFO "423")+226
(define ERR-NONICKNAMEGIVEN "431")+227
(define ERR-ERRONEUSNICKNAME "432")+228
(define ERR-NICKNAMEINUSE "433")+229
(define ERR-NICKCOLLISION "436")+230
(define ERR-USERNOTINCHANNEL "441")+231
(define ERR-NOTONCHANNEL "442")+232
(define ERR-USERONCHANNEL "443")+233
(define ERR-NOLOGIN "444")+234
(define ERR-NOTREGISTERED "451")+235
(define ERR-NEEDMOREPARAMS "461")+236
(define ERR-ALREADYREGISTERED "462")+237
(define ERR-PASSWDMISMATCH "464")+238
(define ERR-YOUREBANNEDCREEP "465")+239
(define ERR-CHANNELISFULL "471")+240
(define ERR-UNKNOWNMODE "472")+241
(define ERR-INVITEONLYCHAN "473")+242
(define ERR-BANNEDFROMCHAN "474")+243
(define ERR-BADCHANNELKEY "475")+244
(define ERR-BADCHANMASK "476")+245
(define ERR-NOCHANMODES "477")+246
(define ERR-CHANOPRIVSNEEDED "482")+247
(define ERR-CANTKILLSERVER "483")+248
(define ERR-NOOPERHOST "491")+249
(define ERR-UMODEUNKNOWNFLAG "501")+250
(define ERR-USERSDONTMATCH "502")+251
+252
(define RPL-LOGGEDIN "900")+253
(define RPL-LOGGEDOUT "901")+254
(define ERR-NICKLOCKED "902")+255
(define RPL-SASLSUCCESS "903")+256
(define ERR-SASLFAIL "904")+257
(define ERR-SASLTOOLONG "905")+258
(define ERR-SASLABORTED "906")+259
(define ERR-SASLALREADY "907")+260
(define RPL-SASLMECHS "908")+261
+262
(define RPL-MONONLINE "730")+263
(define RPL-MONOFFLINE "731")+264
(define RPL-MONLIST "732")+265
(define RPL-ENDOFMONLIST "733")+266
(define ERR-MONLISTFULL "734")+267
+268
(define RPL-STARTTLS "670")+269
(define ERR-STARTTLS "691")+270
+271
(define CMD-FAIL "FAIL")+272
(define CMD-WARN "WARN")+273
(define CMD-NOTE "NOTE")+274
+275
+276
;; ============================================================+277
;; Lookup helpers+278
;; ============================================================+279
+280
;;; Map a numeric reply code (string or symbol form) to its canonical+281
;;; constant name as a symbol. Returns `#f` for unknown codes.+282
;;;+283
;;; ```scheme+284
;;; (irc-numeric-name "001") ; => 'RPL-WELCOME+285
;;; (irc-numeric-name '|001|) ; => 'RPL-WELCOME+286
;;; ```+287
(define (irc-numeric-name code)+288
(: any? -> any?)+289
(let ((s (cond ((string? code) code)+290
((symbol? code) (symbol->string code))+291
(else #f))))+292
(cond+293
((not s) #f)+294
((equal? s "001") 'RPL-WELCOME)+295
((equal? s "002") 'RPL-YOURHOST)+296
((equal? s "003") 'RPL-CREATED)+297
((equal? s "004") 'RPL-MYINFO)+298
((equal? s "005") 'RPL-ISUPPORT)+299
((equal? s "251") 'RPL-LUSERCLIENT)+300
((equal? s "252") 'RPL-LUSEROP)+301
((equal? s "253") 'RPL-LUSERUNKNOWN)+302
((equal? s "254") 'RPL-LUSERCHANNELS)+303
((equal? s "255") 'RPL-LUSERME)+304
((equal? s "301") 'RPL-AWAY)+305
((equal? s "311") 'RPL-WHOISUSER)+306
((equal? s "315") 'RPL-ENDOFWHO)+307
((equal? s "318") 'RPL-ENDOFWHOIS)+308
((equal? s "330") 'RPL-WHOISACCOUNT)+309
((equal? s "332") 'RPL-TOPIC)+310
((equal? s "333") 'RPL-TOPICWHOTIME)+311
((equal? s "352") 'RPL-WHOREPLY)+312
((equal? s "353") 'RPL-NAMREPLY)+313
((equal? s "366") 'RPL-ENDOFNAMES)+314
((equal? s "375") 'RPL-MOTDSTART)+315
((equal? s "372") 'RPL-MOTD)+316
((equal? s "376") 'RPL-ENDOFMOTD)+317
((equal? s "401") 'ERR-NOSUCHNICK)+318
((equal? s "403") 'ERR-NOSUCHCHANNEL)+319
((equal? s "421") 'ERR-UNKNOWNCOMMAND)+320
((equal? s "431") 'ERR-NONICKNAMEGIVEN)+321
((equal? s "432") 'ERR-ERRONEUSNICKNAME)+322
((equal? s "433") 'ERR-NICKNAMEINUSE)+323
((equal? s "451") 'ERR-NOTREGISTERED)+324
((equal? s "461") 'ERR-NEEDMOREPARAMS)+325
((equal? s "462") 'ERR-ALREADYREGISTERED)+326
((equal? s "464") 'ERR-PASSWDMISMATCH)+327
((equal? s "473") 'ERR-INVITEONLYCHAN)+328
((equal? s "475") 'ERR-BADCHANNELKEY)+329
((equal? s "482") 'ERR-CHANOPRIVSNEEDED)+330
((equal? s "900") 'RPL-LOGGEDIN)+331
((equal? s "901") 'RPL-LOGGEDOUT)+332
((equal? s "902") 'ERR-NICKLOCKED)+333
((equal? s "903") 'RPL-SASLSUCCESS)+334
((equal? s "904") 'ERR-SASLFAIL)+335
((equal? s "905") 'ERR-SASLTOOLONG)+336
((equal? s "906") 'ERR-SASLABORTED)+337
((equal? s "907") 'ERR-SASLALREADY)+338
((equal? s "908") 'RPL-SASLMECHS)+339
((equal? s "730") 'RPL-MONONLINE)+340
((equal? s "731") 'RPL-MONOFFLINE)+341
((equal? s "732") 'RPL-MONLIST)+342
((equal? s "733") 'RPL-ENDOFMONLIST)+343
((equal? s "734") 'ERR-MONLISTFULL)+344
(else #f))))+345
+346
;;; Convert a numeric code string ("001") to the parser's symbol form+347
;;; (`'|001|`). Useful for `eq?` comparisons against+348
;;; `irc-message-command`.+349
(define (irc-numeric->symbol code)+350
(: string? -> symbol?)+351
(string->symbol code))+352
+353
))src/sigil/irc/tags.sgladded
@@ -0,0 +1,137 @@
+1
;;; (sigil irc tags) - typed accessors for common IRCv3 message tags+2
;;;+3
;;; Convenience accessors for the well-known tags that show up across+4
;;; Tier 1 + Tier 2 capabilities. The raw tags alist is on every+5
;;; irc-message; these helpers wrap `irc-message-tag` with type-aware+6
;;; lookups and known constant names.+7
;;;+8
;;; All accessors return:+9
;;; - the parsed value when the tag is present+10
;;; - `#f` when the tag is missing+11
;;; - the symbol `'flag` when the tag is present but valueless+12
;;; (only `echo-message` realistically does this)+13
+14
(define-library (sigil irc tags)+15
(import (sigil core)+16
(sigil string)+17
(sigil irc message))+18
+19
(export+20
;; Tag name constants+21
TAG-TIME+22
TAG-MSGID+23
TAG-ACCOUNT+24
TAG-BATCH+25
TAG-LABEL+26
TAG-REPLY+27
TAG-TYPING+28
+29
;; Typed accessors+30
msg-time+31
msg-msgid+32
msg-account+33
msg-batch+34
msg-label+35
msg-reply+36
msg-typing+37
+38
;; Builders for outbound tag alists+39
with-time+40
with-msgid+41
with-account+42
with-batch+43
with-label)+44
+45
(begin+46
+47
(define TAG-TIME "time")+48
(define TAG-MSGID "msgid")+49
(define TAG-ACCOUNT "account")+50
(define TAG-BATCH "batch")+51
(define TAG-LABEL "label")+52
(define TAG-REPLY "+draft/reply")+53
(define TAG-TYPING "+typing")+54
+55
+56
;; ============================================================+57
;; Typed accessors+58
;; ============================================================+59
+60
;;; The `server-time` tag's value (RFC 3339 UTC string), or `#f`.+61
(define (msg-time msg)+62
(: irc-message? -> any?)+63
(let ((v (irc-message-tag msg TAG-TIME)))+64
(and (string? v) v)))+65
+66
;;; The `msgid` tag's value, or `#f`.+67
(define (msg-msgid msg)+68
(: irc-message? -> any?)+69
(let ((v (irc-message-tag msg TAG-MSGID)))+70
(and (string? v) v)))+71
+72
;;; The `account` tag's value (account name), or `#f`. May be the+73
;;; literal string `"*"` when `account-tag` is enabled but the+74
;;; sender has no logged-in account.+75
(define (msg-account msg)+76
(: irc-message? -> any?)+77
(let ((v (irc-message-tag msg TAG-ACCOUNT)))+78
(and (string? v) v)))+79
+80
;;; The `batch` tag's value (batch reftag), or `#f`.+81
(define (msg-batch msg)+82
(: irc-message? -> any?)+83
(let ((v (irc-message-tag msg TAG-BATCH)))+84
(and (string? v) v)))+85
+86
;;; The `label` tag's value (labeled-response correlation id), or `#f`.+87
(define (msg-label msg)+88
(: irc-message? -> any?)+89
(let ((v (irc-message-tag msg TAG-LABEL)))+90
(and (string? v) v)))+91
+92
;;; The `+draft/reply` tag's value (reply-to msgid), or `#f`.+93
(define (msg-reply msg)+94
(: irc-message? -> any?)+95
(let ((v (irc-message-tag msg TAG-REPLY)))+96
(and (string? v) v)))+97
+98
;;; The `+typing` tag's value (`active` / `paused` / `done`), or `#f`.+99
(define (msg-typing msg)+100
(: irc-message? -> any?)+101
(let ((v (irc-message-tag msg TAG-TYPING)))+102
(and (string? v) v)))+103
+104
+105
;; ============================================================+106
;; Builders — add a tag entry to an existing alist+107
;; ============================================================+108
+109
(define (set-tag tags k v)+110
(cons (cons k v) (filter (lambda (e) (not (equal? (car e) k))) tags)))+111
+112
;;; Add or replace the `time` tag.+113
(define (with-time tags ts)+114
(: list? string? -> list?)+115
(set-tag tags TAG-TIME ts))+116
+117
;;; Add or replace the `msgid` tag.+118
(define (with-msgid tags mid)+119
(: list? string? -> list?)+120
(set-tag tags TAG-MSGID mid))+121
+122
;;; Add or replace the `account` tag.+123
(define (with-account tags account)+124
(: list? string? -> list?)+125
(set-tag tags TAG-ACCOUNT account))+126
+127
;;; Add or replace the `batch` tag.+128
(define (with-batch tags reftag)+129
(: list? string? -> list?)+130
(set-tag tags TAG-BATCH reftag))+131
+132
;;; Add or replace the `label` tag.+133
(define (with-label tags label)+134
(: list? string? -> list?)+135
(set-tag tags TAG-LABEL label))+136
+137
))test/test-numerics.sgladded
@@ -0,0 +1,57 @@
+1
;;; Tests for numeric reply constants and lookup helpers.+2
+3
(import (sigil test)+4
(sigil irc numerics))+5
+6
(test-group "Numeric reply constants"+7
+8
(test "registration block"+9
(assert-equal "001" RPL-WELCOME)+10
(assert-equal "002" RPL-YOURHOST)+11
(assert-equal "003" RPL-CREATED)+12
(assert-equal "004" RPL-MYINFO)+13
(assert-equal "005" RPL-ISUPPORT))+14
+15
(test "channel block"+16
(assert-equal "332" RPL-TOPIC)+17
(assert-equal "353" RPL-NAMREPLY)+18
(assert-equal "366" RPL-ENDOFNAMES))+19
+20
(test "common errors"+21
(assert-equal "401" ERR-NOSUCHNICK)+22
(assert-equal "421" ERR-UNKNOWNCOMMAND)+23
(assert-equal "433" ERR-NICKNAMEINUSE)+24
(assert-equal "461" ERR-NEEDMOREPARAMS))+25
+26
(test "SASL block"+27
(assert-equal "900" RPL-LOGGEDIN)+28
(assert-equal "903" RPL-SASLSUCCESS)+29
(assert-equal "904" ERR-SASLFAIL))+30
+31
(test "MONITOR block"+32
(assert-equal "730" RPL-MONONLINE)+33
(assert-equal "731" RPL-MONOFFLINE)+34
(assert-equal "733" RPL-ENDOFMONLIST)))+35
+36
+37
(test-group "irc-numeric-name lookup"+38
+39
(test "string form lookup"+40
(assert-equal 'RPL-WELCOME (irc-numeric-name "001"))+41
(assert-equal 'ERR-NICKNAMEINUSE (irc-numeric-name "433"))+42
(assert-equal 'RPL-SASLSUCCESS (irc-numeric-name "903")))+43
+44
(test "symbol form lookup"+45
(assert-equal 'RPL-WELCOME (irc-numeric-name (string->symbol "001")))+46
(assert-equal 'RPL-MONONLINE (irc-numeric-name (string->symbol "730"))))+47
+48
(test "unknown returns #f"+49
(assert-equal #f (irc-numeric-name "999"))))+50
+51
+52
(test-group "irc-numeric->symbol"+53
+54
(test "wraps via string->symbol"+55
(assert-equal (string->symbol "001") (irc-numeric->symbol "001"))))+56
+57
(run-tests)