Repository: biwascheme/biwascheme
Pull request #344 (2024-04-17 09:11:11)

Add _getToken() to parser
This PR fixes several bugs (#341 #337 #330) of the parser.


Patch:
diff --git a/package-lock.json b/package-lock.json
index 12880a5..e034405 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1818,27 +1818,24 @@
             }
         },
         "node_modules/body-parser": {
-            "version": "1.20.2",
-            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
-            "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
+            "version": "1.19.2",
+            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz",
+            "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==",
             "dev": true,
             "dependencies": {
                 "bytes": "3.1.2",
-                "content-type": "~1.0.5",
+                "content-type": "~1.0.4",
                 "debug": "2.6.9",
-                "depd": "2.0.0",
-                "destroy": "1.2.0",
-                "http-errors": "2.0.0",
+                "depd": "~1.1.2",
+                "http-errors": "1.8.1",
                 "iconv-lite": "0.4.24",
-                "on-finished": "2.4.1",
-                "qs": "6.11.0",
-                "raw-body": "2.5.2",
-                "type-is": "~1.6.18",
-                "unpipe": "1.0.0"
+                "on-finished": "~2.3.0",
+                "qs": "6.9.7",
+                "raw-body": "2.4.3",
+                "type-is": "~1.6.18"
             },
             "engines": {
-                "node": ">= 0.8",
-                "npm": "1.2.8000 || >= 1.4.16"
+                "node": ">= 0.8"
             }
         },
         "node_modules/brace-expansion": {
@@ -1952,25 +1949,6 @@
                 "node": ">= 0.8"
             }
         },
-        "node_modules/call-bind": {
-            "version": "1.0.7",
-            "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
-            "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
-            "dev": true,
-            "dependencies": {
-                "es-define-property": "^1.0.0",
-                "es-errors": "^1.3.0",
-                "function-bind": "^1.1.2",
-                "get-intrinsic": "^1.2.4",
-                "set-function-length": "^1.2.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/ljharb"
-            }
-        },
         "node_modules/callsites": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -2138,9 +2116,9 @@
             }
         },
         "node_modules/content-type": {
-            "version": "1.0.5",
-            "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
-            "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+            "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
             "dev": true,
             "engines": {
                 "node": ">= 0.6"
@@ -2153,9 +2131,9 @@
             "dev": true
         },
         "node_modules/cookie": {
-            "version": "0.6.0",
-            "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
-            "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
+            "version": "0.4.2",
+            "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+            "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
             "dev": true,
             "engines": {
                 "node": ">= 0.6"
@@ -2313,41 +2291,20 @@
                 "node": ">=0.10.0"
             }
         },
-        "node_modules/define-data-property": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
-            "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
-            "dev": true,
-            "dependencies": {
-                "es-define-property": "^1.0.0",
-                "es-errors": "^1.3.0",
-                "gopd": "^1.0.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/ljharb"
-            }
-        },
         "node_modules/depd": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
-            "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+            "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
             "dev": true,
             "engines": {
-                "node": ">= 0.8"
+                "node": ">= 0.6"
             }
         },
         "node_modules/destroy": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
-            "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
-            "dev": true,
-            "engines": {
-                "node": ">= 0.8",
-                "npm": "1.2.8000 || >= 1.4.16"
-            }
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+            "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==",
+            "dev": true
         },
         "node_modules/detect-newline": {
             "version": "3.1.0",
@@ -2385,7 +2342,7 @@
         "node_modules/ee-first": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-            "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+            "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
             "dev": true
         },
         "node_modules/electron-to-chromium": {
@@ -2415,7 +2372,7 @@
         "node_modules/encodeurl": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-            "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+            "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
             "dev": true,
             "engines": {
                 "node": ">= 0.8"
@@ -2439,27 +2396,6 @@
                 "is-arrayish": "^0.2.1"
             }
         },
-        "node_modules/es-define-property": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
-            "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
-            "dev": true,
-            "dependencies": {
-                "get-intrinsic": "^1.2.4"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
-        "node_modules/es-errors": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
-            "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
-            "dev": true,
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
         "node_modules/escalade": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -2472,7 +2408,7 @@
         "node_modules/escape-html": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-            "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+            "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
             "dev": true
         },
         "node_modules/escape-string-regexp": {
@@ -2573,39 +2509,38 @@
             }
         },
         "node_modules/express": {
-            "version": "4.19.2",
-            "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
-            "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
+            "version": "4.17.3",
+            "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz",
+            "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==",
             "dev": true,
             "dependencies": {
                 "accepts": "~1.3.8",
                 "array-flatten": "1.1.1",
-                "body-parser": "1.20.2",
+                "body-parser": "1.19.2",
                 "content-disposition": "0.5.4",
                 "content-type": "~1.0.4",
-                "cookie": "0.6.0",
+                "cookie": "0.4.2",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
-                "depd": "2.0.0",
+                "depd": "~1.1.2",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "etag": "~1.8.1",
-                "finalhandler": "1.2.0",
+                "finalhandler": "~1.1.2",
                 "fresh": "0.5.2",
-                "http-errors": "2.0.0",
                 "merge-descriptors": "1.0.1",
                 "methods": "~1.1.2",
-                "on-finished": "2.4.1",
+                "on-finished": "~2.3.0",
                 "parseurl": "~1.3.3",
                 "path-to-regexp": "0.1.7",
                 "proxy-addr": "~2.0.7",
-                "qs": "6.11.0",
+                "qs": "6.9.7",
                 "range-parser": "~1.2.1",
                 "safe-buffer": "5.2.1",
-                "send": "0.18.0",
-                "serve-static": "1.15.0",
+                "send": "0.17.2",
+                "serve-static": "1.14.2",
                 "setprototypeof": "1.2.0",
-                "statuses": "2.0.1",
+                "statuses": "~1.5.0",
                 "type-is": "~1.6.18",
                 "utils-merge": "1.0.1",
                 "vary": "~1.1.2"
@@ -2694,17 +2629,17 @@
             }
         },
         "node_modules/finalhandler": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
-            "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+            "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
             "dev": true,
             "dependencies": {
                 "debug": "2.6.9",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
-                "on-finished": "2.4.1",
+                "on-finished": "~2.3.0",
                 "parseurl": "~1.3.3",
-                "statuses": "2.0.1",
+                "statuses": "~1.5.0",
                 "unpipe": "~1.0.0"
             },
             "engines": {
@@ -2794,25 +2729,6 @@
                 "node": "6.* || 8.* || >= 10.*"
             }
         },
-        "node_modules/get-intrinsic": {
-            "version": "1.2.4",
-            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
-            "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
-            "dev": true,
-            "dependencies": {
-                "es-errors": "^1.3.0",
-                "function-bind": "^1.1.2",
-                "has-proto": "^1.0.1",
-                "has-symbols": "^1.0.3",
-                "hasown": "^2.0.0"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/ljharb"
-            }
-        },
         "node_modules/get-package-type": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
@@ -2875,18 +2791,6 @@
                 "node": ">=4"
             }
         },
-        "node_modules/gopd": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
-            "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
-            "dev": true,
-            "dependencies": {
-                "get-intrinsic": "^1.1.3"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/ljharb"
-            }
-        },
         "node_modules/graceful-fs": {
             "version": "4.2.11",
             "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -2902,42 +2806,6 @@
                 "node": ">=4"
             }
         },
-        "node_modules/has-property-descriptors": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
-            "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
-            "dev": true,
-            "dependencies": {
-                "es-define-property": "^1.0.0"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/ljharb"
-            }
-        },
-        "node_modules/has-proto": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
-            "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
-            "dev": true,
-            "engines": {
-                "node": ">= 0.4"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/ljharb"
-            }
-        },
-        "node_modules/has-symbols": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
-            "dev": true,
-            "engines": {
-                "node": ">= 0.4"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/ljharb"
-            }
-        },
         "node_modules/hasown": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
@@ -2957,19 +2825,19 @@
             "dev": true
         },
         "node_modules/http-errors": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
-            "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+            "version": "1.8.1",
+            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+            "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
             "dev": true,
             "dependencies": {
-                "depd": "2.0.0",
+                "depd": "~1.1.2",
                 "inherits": "2.0.4",
                 "setprototypeof": "1.2.0",
-                "statuses": "2.0.1",
+                "statuses": ">= 1.5.0 < 2",
                 "toidentifier": "1.0.1"
             },
             "engines": {
-                "node": ">= 0.8"
+                "node": ">= 0.6"
             }
         },
         "node_modules/https-proxy-agent": {
@@ -5410,19 +5278,10 @@
                 "node": ">=8"
             }
         },
-        "node_modules/object-inspect": {
-            "version": "1.13.1",
-            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
-            "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
-            "dev": true,
-            "funding": {
-                "url": "https://github.com/sponsors/ljharb"
-            }
-        },
         "node_modules/on-finished": {
-            "version": "2.4.1",
-            "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
-            "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+            "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
             "dev": true,
             "dependencies": {
                 "ee-first": "1.1.1"
@@ -5760,13 +5619,10 @@
             ]
         },
         "node_modules/qs": {
-            "version": "6.11.0",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
-            "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+            "version": "6.9.7",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
+            "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
             "dev": true,
-            "dependencies": {
-                "side-channel": "^1.0.4"
-            },
             "engines": {
                 "node": ">=0.6"
             },
@@ -5793,13 +5649,13 @@
             }
         },
         "node_modules/raw-body": {
-            "version": "2.5.2",
-            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
-            "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+            "version": "2.4.3",
+            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz",
+            "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==",
             "dev": true,
             "dependencies": {
                 "bytes": "3.1.2",
-                "http-errors": "2.0.0",
+                "http-errors": "1.8.1",
                 "iconv-lite": "0.4.24",
                 "unpipe": "1.0.0"
             },
@@ -5999,24 +5855,24 @@
             }
         },
         "node_modules/send": {
-            "version": "0.18.0",
-            "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
-            "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+            "version": "0.17.2",
+            "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
+            "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
             "dev": true,
             "dependencies": {
                 "debug": "2.6.9",
-                "depd": "2.0.0",
-                "destroy": "1.2.0",
+                "depd": "~1.1.2",
+                "destroy": "~1.0.4",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "etag": "~1.8.1",
                 "fresh": "0.5.2",
-                "http-errors": "2.0.0",
+                "http-errors": "1.8.1",
                 "mime": "1.6.0",
                 "ms": "2.1.3",
-                "on-finished": "2.4.1",
+                "on-finished": "~2.3.0",
                 "range-parser": "~1.2.1",
-                "statuses": "2.0.1"
+                "statuses": "~1.5.0"
             },
             "engines": {
                 "node": ">= 0.8.0"
@@ -6038,37 +5894,20 @@
             }
         },
         "node_modules/serve-static": {
-            "version": "1.15.0",
-            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
-            "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+            "version": "1.14.2",
+            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz",
+            "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==",
             "dev": true,
             "dependencies": {
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "parseurl": "~1.3.3",
-                "send": "0.18.0"
+                "send": "0.17.2"
             },
             "engines": {
                 "node": ">= 0.8.0"
             }
         },
-        "node_modules/set-function-length": {
-            "version": "1.2.2",
-            "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
-            "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
-            "dev": true,
-            "dependencies": {
-                "define-data-property": "^1.1.4",
-                "es-errors": "^1.3.0",
-                "function-bind": "^1.1.2",
-                "get-intrinsic": "^1.2.4",
-                "gopd": "^1.0.1",
-                "has-property-descriptors": "^1.0.2"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            }
-        },
         "node_modules/setprototypeof": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -6096,24 +5935,6 @@
                 "node": ">=8"
             }
         },
-        "node_modules/side-channel": {
-            "version": "1.0.6",
-            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
-            "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
-            "dev": true,
-            "dependencies": {
-                "call-bind": "^1.0.7",
-                "es-errors": "^1.3.0",
-                "get-intrinsic": "^1.2.4",
-                "object-inspect": "^1.13.1"
-            },
-            "engines": {
-                "node": ">= 0.4"
-            },
-            "funding": {
-                "url": "https://github.com/sponsors/ljharb"
-            }
-        },
         "node_modules/signal-exit": {
             "version": "3.0.7",
             "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -6236,12 +6057,12 @@
             }
         },
         "node_modules/statuses": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
-            "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+            "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
             "dev": true,
             "engines": {
-                "node": ">= 0.8"
+                "node": ">= 0.6"
             }
         },
         "node_modules/string_decoder": {
@@ -6519,7 +6340,7 @@
         "node_modules/unpipe": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-            "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+            "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
             "dev": true,
             "engines": {
                 "node": ">= 0.8"
@@ -8171,23 +7992,21 @@
             }
         },
         "body-parser": {
-            "version": "1.20.2",
-            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
-            "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
+            "version": "1.19.2",
+            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz",
+            "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==",
             "dev": true,
             "requires": {
                 "bytes": "3.1.2",
-                "content-type": "~1.0.5",
+                "content-type": "~1.0.4",
                 "debug": "2.6.9",
-                "depd": "2.0.0",
-                "destroy": "1.2.0",
-                "http-errors": "2.0.0",
+                "depd": "~1.1.2",
+                "http-errors": "1.8.1",
                 "iconv-lite": "0.4.24",
-                "on-finished": "2.4.1",
-                "qs": "6.11.0",
-                "raw-body": "2.5.2",
-                "type-is": "~1.6.18",
-                "unpipe": "1.0.0"
+                "on-finished": "~2.3.0",
+                "qs": "6.9.7",
+                "raw-body": "2.4.3",
+                "type-is": "~1.6.18"
             }
         },
         "brace-expansion": {
@@ -8258,19 +8077,6 @@
             "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
             "dev": true
         },
-        "call-bind": {
-            "version": "1.0.7",
-            "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
-            "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
-            "dev": true,
-            "requires": {
-                "es-define-property": "^1.0.0",
-                "es-errors": "^1.3.0",
-                "function-bind": "^1.1.2",
-                "get-intrinsic": "^1.2.4",
-                "set-function-length": "^1.2.1"
-            }
-        },
         "callsites": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -8390,9 +8196,9 @@
             }
         },
         "content-type": {
-            "version": "1.0.5",
-            "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
-            "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+            "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
             "dev": true
         },
         "convert-source-map": {
@@ -8402,9 +8208,9 @@
             "dev": true
         },
         "cookie": {
-            "version": "0.6.0",
-            "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
-            "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
+            "version": "0.4.2",
+            "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+            "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
             "dev": true
         },
         "cookie-signature": {
@@ -8521,27 +8327,16 @@
             "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
             "dev": true
         },
-        "define-data-property": {
-            "version": "1.1.4",
-            "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
-            "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
-            "dev": true,
-            "requires": {
-                "es-define-property": "^1.0.0",
-                "es-errors": "^1.3.0",
-                "gopd": "^1.0.1"
-            }
-        },
         "depd": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
-            "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+            "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
             "dev": true
         },
         "destroy": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
-            "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+            "version": "1.0.4",
+            "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+            "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==",
             "dev": true
         },
         "detect-newline": {
@@ -8571,7 +8366,7 @@
         "ee-first": {
             "version": "1.1.1",
             "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-            "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+            "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
             "dev": true
         },
         "electron-to-chromium": {
@@ -8595,7 +8390,7 @@
         "encodeurl": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
-            "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+            "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
             "dev": true
         },
         "end-of-stream": {
@@ -8616,21 +8411,6 @@
                 "is-arrayish": "^0.2.1"
             }
         },
-        "es-define-property": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
-            "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
-            "dev": true,
-            "requires": {
-                "get-intrinsic": "^1.2.4"
-            }
-        },
-        "es-errors": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
-            "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
-            "dev": true
-        },
         "escalade": {
             "version": "3.1.1",
             "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -8640,7 +8420,7 @@
         "escape-html": {
             "version": "1.0.3",
             "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-            "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+            "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
             "dev": true
         },
         "escape-string-regexp": {
@@ -8712,39 +8492,38 @@
             }
         },
         "express": {
-            "version": "4.19.2",
-            "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
-            "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
+            "version": "4.17.3",
+            "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz",
+            "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==",
             "dev": true,
             "requires": {
                 "accepts": "~1.3.8",
                 "array-flatten": "1.1.1",
-                "body-parser": "1.20.2",
+                "body-parser": "1.19.2",
                 "content-disposition": "0.5.4",
                 "content-type": "~1.0.4",
-                "cookie": "0.6.0",
+                "cookie": "0.4.2",
                 "cookie-signature": "1.0.6",
                 "debug": "2.6.9",
-                "depd": "2.0.0",
+                "depd": "~1.1.2",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "etag": "~1.8.1",
-                "finalhandler": "1.2.0",
+                "finalhandler": "~1.1.2",
                 "fresh": "0.5.2",
-                "http-errors": "2.0.0",
                 "merge-descriptors": "1.0.1",
                 "methods": "~1.1.2",
-                "on-finished": "2.4.1",
+                "on-finished": "~2.3.0",
                 "parseurl": "~1.3.3",
                 "path-to-regexp": "0.1.7",
                 "proxy-addr": "~2.0.7",
-                "qs": "6.11.0",
+                "qs": "6.9.7",
                 "range-parser": "~1.2.1",
                 "safe-buffer": "5.2.1",
-                "send": "0.18.0",
-                "serve-static": "1.15.0",
+                "send": "0.17.2",
+                "serve-static": "1.14.2",
                 "setprototypeof": "1.2.0",
-                "statuses": "2.0.1",
+                "statuses": "~1.5.0",
                 "type-is": "~1.6.18",
                 "utils-merge": "1.0.1",
                 "vary": "~1.1.2"
@@ -8813,17 +8592,17 @@
             }
         },
         "finalhandler": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
-            "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+            "version": "1.1.2",
+            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+            "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
             "dev": true,
             "requires": {
                 "debug": "2.6.9",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
-                "on-finished": "2.4.1",
+                "on-finished": "~2.3.0",
                 "parseurl": "~1.3.3",
-                "statuses": "2.0.1",
+                "statuses": "~1.5.0",
                 "unpipe": "~1.0.0"
             }
         },
@@ -8886,19 +8665,6 @@
             "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
             "dev": true
         },
-        "get-intrinsic": {
-            "version": "1.2.4",
-            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
-            "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
-            "dev": true,
-            "requires": {
-                "es-errors": "^1.3.0",
-                "function-bind": "^1.1.2",
-                "has-proto": "^1.0.1",
-                "has-symbols": "^1.0.3",
-                "hasown": "^2.0.0"
-            }
-        },
         "get-package-type": {
             "version": "0.1.0",
             "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
@@ -8940,15 +8706,6 @@
             "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
             "dev": true
         },
-        "gopd": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
-            "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
-            "dev": true,
-            "requires": {
-                "get-intrinsic": "^1.1.3"
-            }
-        },
         "graceful-fs": {
             "version": "4.2.11",
             "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -8961,27 +8718,6 @@
             "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
             "dev": true
         },
-        "has-property-descriptors": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
-            "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
-            "dev": true,
-            "requires": {
-                "es-define-property": "^1.0.0"
-            }
-        },
-        "has-proto": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
-            "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
-            "dev": true
-        },
-        "has-symbols": {
-            "version": "1.0.3",
-            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
-            "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
-            "dev": true
-        },
         "hasown": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
@@ -8998,15 +8734,15 @@
             "dev": true
         },
         "http-errors": {
-            "version": "2.0.0",
-            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
-            "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+            "version": "1.8.1",
+            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+            "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
             "dev": true,
             "requires": {
-                "depd": "2.0.0",
+                "depd": "~1.1.2",
                 "inherits": "2.0.4",
                 "setprototypeof": "1.2.0",
-                "statuses": "2.0.1",
+                "statuses": ">= 1.5.0 < 2",
                 "toidentifier": "1.0.1"
             }
         },
@@ -10812,16 +10548,10 @@
                 "path-key": "^3.0.0"
             }
         },
-        "object-inspect": {
-            "version": "1.13.1",
-            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
-            "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
-            "dev": true
-        },
         "on-finished": {
-            "version": "2.4.1",
-            "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
-            "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+            "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
             "dev": true,
             "requires": {
                 "ee-first": "1.1.1"
@@ -11066,13 +10796,10 @@
             "dev": true
         },
         "qs": {
-            "version": "6.11.0",
-            "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
-            "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
-            "dev": true,
-            "requires": {
-                "side-channel": "^1.0.4"
-            }
+            "version": "6.9.7",
+            "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
+            "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
+            "dev": true
         },
         "randombytes": {
             "version": "2.1.0",
@@ -11090,13 +10817,13 @@
             "dev": true
         },
         "raw-body": {
-            "version": "2.5.2",
-            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
-            "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+            "version": "2.4.3",
+            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz",
+            "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==",
             "dev": true,
             "requires": {
                 "bytes": "3.1.2",
-                "http-errors": "2.0.0",
+                "http-errors": "1.8.1",
                 "iconv-lite": "0.4.24",
                 "unpipe": "1.0.0"
             }
@@ -11234,24 +10961,24 @@
             "dev": true
         },
         "send": {
-            "version": "0.18.0",
-            "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
-            "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+            "version": "0.17.2",
+            "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz",
+            "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==",
             "dev": true,
             "requires": {
                 "debug": "2.6.9",
-                "depd": "2.0.0",
-                "destroy": "1.2.0",
+                "depd": "~1.1.2",
+                "destroy": "~1.0.4",
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "etag": "~1.8.1",
                 "fresh": "0.5.2",
-                "http-errors": "2.0.0",
+                "http-errors": "1.8.1",
                 "mime": "1.6.0",
                 "ms": "2.1.3",
-                "on-finished": "2.4.1",
+                "on-finished": "~2.3.0",
                 "range-parser": "~1.2.1",
-                "statuses": "2.0.1"
+                "statuses": "~1.5.0"
             },
             "dependencies": {
                 "ms": {
@@ -11272,29 +10999,15 @@
             }
         },
         "serve-static": {
-            "version": "1.15.0",
-            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
-            "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+            "version": "1.14.2",
+            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz",
+            "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==",
             "dev": true,
             "requires": {
                 "encodeurl": "~1.0.2",
                 "escape-html": "~1.0.3",
                 "parseurl": "~1.3.3",
-                "send": "0.18.0"
-            }
-        },
-        "set-function-length": {
-            "version": "1.2.2",
-            "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
-            "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
-            "dev": true,
-            "requires": {
-                "define-data-property": "^1.1.4",
-                "es-errors": "^1.3.0",
-                "function-bind": "^1.1.2",
-                "get-intrinsic": "^1.2.4",
-                "gopd": "^1.0.1",
-                "has-property-descriptors": "^1.0.2"
+                "send": "0.17.2"
             }
         },
         "setprototypeof": {
@@ -11318,18 +11031,6 @@
             "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
             "dev": true
         },
-        "side-channel": {
-            "version": "1.0.6",
-            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
-            "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
-            "dev": true,
-            "requires": {
-                "call-bind": "^1.0.7",
-                "es-errors": "^1.3.0",
-                "get-intrinsic": "^1.2.4",
-                "object-inspect": "^1.13.1"
-            }
-        },
         "signal-exit": {
             "version": "3.0.7",
             "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
@@ -11429,9 +11130,9 @@
             }
         },
         "statuses": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
-            "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+            "version": "1.5.0",
+            "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+            "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
             "dev": true
         },
         "string_decoder": {
@@ -11640,7 +11341,7 @@
         "unpipe": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
-            "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+            "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
             "dev": true
         },
         "update-browserslist-db": {
diff --git a/src/system/parser.js b/src/system/parser.js
index b1d5cef..0aa60b7 100644
--- a/src/system/parser.js
+++ b/src/system/parser.js
@@ -1,5 +1,5 @@
 import { nil } from "../header.js"
-import { inspect } from "./_writer.js"
+import { inspect, to_write } from "./_writer.js"
 import { Char } from "./char.js"
 import { BiwaError, Bug } from "./error.js"
 import { Pair, List } from "./pair.js"
@@ -11,6 +11,13 @@ import { Complex, Rational } from "./number.js"
 class Unterminated extends BiwaError {}
 // Raised when input contains extraneous `)`, etc.
 class Unbalanced extends BiwaError {}
+// Raised for invalid char literal, etc.
+class Invalid extends BiwaError {}
+
+// Matches first word-like stuff i.e. characters not space and
+// not symbols that are not allowed for an identifier.
+// TODO: is there other non-id symbols in Unicode?
+const ID_REXP = /^[^\s\t\n\(\)\[\]\{\}\"\'\`\,\;\|]+/
 
 // Allowed digits in each base
 const DIGITS = {
@@ -39,10 +46,6 @@ const NAMED_CHARS = {
   space: " ",
   tab: "\t",
 }
-const NAMED_CHARS_REXP = new RegExp(
-  "^(" + 
-  Object.keys(NAMED_CHARS).join("|") + 
-  ")\\b");
 
 // Escape sequences like `\n`, `\t`
 const ESCAPE_SEQUENCES = {
@@ -86,14 +89,12 @@ class Parser {
     if (this.done()) {
       ret = Parser.EOS;
     } else {
-      switch (this.txt[this.i]) {
-        case "#":
-          this.i++;
-          ret = this._parseSharp();
-          break;
+      const tok = this._getToken();
+      switch (tok) {
         case "(":
         case "[":
         case "{":
+          this.i--; // Unget the first paren
           ret = this._parseList();
           break;
         case '"':
@@ -106,34 +107,33 @@ class Parser {
         case ")":
         case "]":
         case "}":
-          throw new Unbalanced(`found extraneous '${this.txt[this.i]}'`);
+          throw new Unbalanced(`found extraneous '${tok}'`);
           break;
 
         // Aliases
         case "'":
-          this.i++;
           ret = this._parseQuote("quote");
           break;
         case "`":
-          this.i++;
           ret = this._parseQuote("quasiquote");
           break;
         case ",":
-          this.i++;
-          if (this.txt[this.i] == "@") {
-            this.i++;
-            ret = this._parseQuote("unquote-splicing");
-          } else {
-            ret = this._parseQuote("unquote");
-          }
+          ret = this._parseQuote("unquote");
+          break;
+        case ",@":
+          ret = this._parseQuote("unquote-splicing");
           break;
 
         default:
-          ret = this._parseDecimalNumberOrIdentifier();
+          if (tok[0] == "#") {
+            ret = this._parseSharp(tok);
+          } else {
+            ret = this._parseDecimalNumberOrIdentifier(tok);
+          }
           break;
       }
     }
-    //console.log("getObject", inspect(ret))
+    //onsole.log("getObject", inspect(ret))
     return ret;
   }
 
@@ -143,6 +143,98 @@ class Parser {
     return v;
   }
 
+  // Returns current token and proceeds the cursor.
+  _getToken() {
+    const c = this.txt[this.i];
+    if (c === undefined) throw new Bug("EOS must be handled beforehand")
+    let tok;
+    switch (c) {
+      case "(":
+      case "[":
+      case "{":
+      case ")":
+      case "]":
+      case "}":
+      case "'":
+      case "`":
+      case '"':
+      case "|":
+        this.i++;
+        tok = c;
+        break;
+      case ",":
+        this.i++;
+        if (this.txt[this.i] == "@") {
+          this.i++;
+          tok = ",@";
+        } else {
+          tok = ",";
+        }
+        break;
+      case "#":
+        this.i++;
+        tok = this._getSharpToken();
+        break;
+      default:
+        const m = this.match(ID_REXP);
+        if (m) {
+          this.i += m[0].length;
+          tok = m[0];
+        } else {
+          this.i++;
+          tok = c;
+        }
+        break;
+    }
+    return tok;
+  }
+
+  // Get the possible part after `#`
+  _getSharpToken() {
+    const c2 = this.txt[this.i];
+    if (c2 === undefined) {
+      // Program ends with an `#` (invalid)
+      return "#";
+    }
+    if (c2 === "(") {
+      this.i++;
+      return "#(";
+    }
+    if (c2 === "\\") {
+      this.i++;
+      return "#\\" + this._getCharToken();
+    } 
+    let m = this.match(/^(\d+)[=#]/); // Datum label
+    if (m) {
+      this.i += m[0].length;
+      return "#" + m[0];
+    }
+    m = this.match(ID_REXP);
+    if (m) {
+      this.i += m[0].length;
+      return "#" + m[0];
+    }
+    // Invalid symbol after `#`
+    this.i++;
+    return "#" + c2;
+  }
+
+  // Get the possible part after `#\`
+  _getCharToken() {
+    if (this.done()) {
+      throw new Unterminated("got EOS on char literal", this.rest(-2));
+    }
+    const m = this.match(/^(\w+)/);
+    if (m) {
+      return m[0];
+    } else {
+      const c = this.txt[this.i];
+      this.i++;
+      return c;
+    }
+  }
+
+
   // Skip whitespace, comment and directive
   // Note: calling this method may change parser state (`this.foldCase`).
   _skipAtmosphere() {
@@ -223,71 +315,53 @@ class Parser {
   }
 
   // Parse stuffs starting with `#` (except those parsed with _skipAtmosphere)
-  _parseSharp() {
-    switch (this.txt[this.i]) {
-      case "t":
-        if (this.match(/^true/)) {
-          this.i += "true".length;
-        } else {
-          this.i++;
-        }
+  _parseSharp(tok) {
+    if (!tok.startsWith("#")) throw new Bug("not a sharp");
+    switch (tok) {
+      case "#t":
+      case "#true":
         return true;
-      case "f":
-        if (this.match(/^false/)) {
-          this.i += "false".length;
-        } else {
-          this.i++;
-        }
+      case "#f":
+      case "#false":
         return false;
-      case "\\":
-        this.i++;
-        return this._parseChar();
-      case "(":
-        this.i++;
+      case "#(":
         return this._parseVector();
-      case "u":
-        if (this.match(/^u8\(/)) {
-          throw new BiwaError("bytevectors are not supported yet", this.rest(-1));
-        } else {
-          break;
-        }
-      case "e":
-      case "i":
-      case "b":
-      case "o":
-      case "d":
-      case "x":
-        this.i--; // Unget `#`
-        return this._parsePrefixedNumber();
       default:
-        if (this.match(/^\d/)) {
-          return this._parseDatumLabel();
-        } else {
-          break;
+        switch (true) {
+          case tok.startsWith("#\\"):
+            return this._parseChar(tok);
+          case tok.startsWith("#u8"):
+            throw new BiwaError("bytevectors are not supported yet", this.rest(-1));
+          case /^#[eibodx]/.test(tok):
+            return this._parsePrefixedNumber(tok);
+          case /^#\d/.test(tok):
+            return this._parseDatumLabel(tok);
+          default:
+            throw new Invalid("unknown #-syntax", tok);
         }
     }
-    throw new BiwaError("unknown #-syntax", this.rest(-1));
   }
 
-  // Parse a character literal after `#\`
-  _parseChar() {
-    let m = this.match(NAMED_CHARS_REXP);
-    if (m) {
-      this.i += m[0].length;
-      return Char.get(NAMED_CHARS[m[1]]);
+  // Parse a character literal (begins with `#\`)
+  _parseChar(tok) {
+    if (!tok.startsWith("#\\")) throw new Bug("not a char");
+    const body = tok.substring(2);
+    // Named char?
+    let c = NAMED_CHARS[body];
+    if (c) {
+      return Char.get(c);
     }
-    m = this.match(/^x([a-zA-Z0-9]+)/);
+    // Specified by code?
+    let m = /^x([a-zA-Z0-9]+)$/.exec(body);
     if (m) {
-      this.i += m[0].length;
       return Char.get(String.fromCharCode(parseInt(m[1], 16)));
     }
-    if (this.done()) {
-      throw new Unterminated("got EOS on char literal", this.rest(-2));
-    } else {
-      const c = this.txt[this.i];
-      this.i++;
-      return Char.get(c);
+    // A character?
+    if (body.length == 1) {
+      return Char.get(body);
     }
+    // Otherwise
+    throw new Invalid(`Invalid char literal: ${tok}`);
   }
 
   // Parse a vector expression after `#(`
@@ -312,138 +386,135 @@ class Parser {
   }
 
   // Parse a number prefixed with `#i`, `#b`, etc.
-  _parsePrefixedNumber() {
+  // Throws error if not a prefixed number.
+  _parsePrefixedNumber(tok) {
     let base = 10; // Decimal is the default
-    if (this.match(/^#[iIeE]/)) this.i += 2; // Exactness is not supported.
-    if      (this.match(/^#[bB]/)) { base = 2; this.i += 2 }
-    else if (this.match(/^#[oO]/)) { base = 8; this.i += 2 }
-    else if (this.match(/^#[dD]/)) { base = 10; this.i += 2 }
-    else if (this.match(/^#[xX]/)) { base = 16; this.i += 2 }
-    if (this.match(/^#[iIeE]/)) this.i += 2; // Exactness is not supported.
-
-    return this._parseComplexNumber(base);
-  }
-
-  // Parse a (possibly) complex number
-  _parseComplexNumber(base) {
-    const a = this._parseRealNumber(base);
-    const c = this.txt[this.i];
-    if (c == "@") {
-      this.i++;
-      return this._parsePolarComplexNumber(a, base);
-    } else if (c == "+" || c == "-") {
-      this.i--; // Unget the sign
-      return this._parseOrthoComplexNumber(a, base);
+    let s = tok;
+    if (s.match(/^#[iIeE]/)) s = s.substring(2); // Exactness is not supported.
+    if      (s.match(/^#[bB]/)) { base = 2;  s = s.substring(2); }
+    else if (s.match(/^#[oO]/)) { base = 8;  s = s.substring(2); }
+    else if (s.match(/^#[dD]/)) { base = 10; s = s.substring(2); }
+    else if (s.match(/^#[xX]/)) { base = 16; s = s.substring(2); }
+    if (s.match(/^#[iIeE]/)) s = s.substring(2); // Exactness is not supported.
+
+    const v = this._parseComplexNumber(base, s);
+    if (v === null) {
+      // Extraneous char after number prefix (like `#dfoo`)
+      throw new Invalid(`invalid number format: %{tok}`);
     } else {
-      // Was not a complex number.
-      return a;
+      return v;
     }
   }
 
-  // Parse a complex number of the form `a@b`
-  _parsePolarComplexNumber(a, base) {
-    const b = this._parseRealNumber(base);
-    return Complex.from_polar(a, b);
-  }
-
-  // Parse a complex number of the form `a+bi`
-  _parseOrthoComplexNumber(a, base) {
-    const b = this._parseRealNumber(base);
-    if (this.match(/^[iI]/)) {
-      this.i++;
-      return new Complex(a, b)
-    } else {
-      throw new BiwaError("invalid complex number format (missing `i`)", this.rest())
+  // Parse a complex (or real) number.
+  // Returns null if not a number.
+  _parseComplexNumber(base, tok) {
+    const [a, rest] = this._parseRealNumber(base, tok);
+    if (a === null) {
+      return null;
+    } else if (rest === "") {
+      return a;
+    }
+    const c = rest[0];
+    if (c == "@") {
+      // Polar complex number? (eg. `1@2`)
+      const [b, rest2] = this._parseRealNumber(base, rest.substring(1));
+      if (b !== null && rest2 === "") {
+        return Complex.from_polar(a, b);
+      }
+    } else if (c == "+" || c == "-") {
+      // Ortho complex number? (eg. `1+2i`)
+      const [b, rest2] = this._parseRealNumber(base, rest);
+      if (b !== null && rest2.match(/^[iI]$/)) {
+        return new Complex(a, b);
+      }
     }
+    return null;
   }
 
-  // Parse a real number in base 2, 8, or 16
-  // If `maybeSymbol` is true, returns a consumed string intead of throwing error
-  // when it is not a number.
-  _parseRealNumber(base, maybeSymbol=false) {
-    if (maybeSymbol && base != 10) throw new Bug("base must be 10 if maybeSymbol");
-    let asSym = "";
+  // Parse a real (or rational) number at the head of `tok`.
+  // Returns the number and the rest of the string.
+  // Returns [null, tok] if not a number.
+  _parseRealNumber(base, tok) {
+    if (tok === "") return [null, tok];
 
-    // Check if it is inf or nan
-    const m = this.match(/^(\+|-)(inf.0|nan.0)/);
+    // inf or nan
+    const m = tok.match(/^(\+|-)(inf.0|nan.0)/);
     if (m) {
-      this.i += "+inf.0".length;
-      return (m[2] == "inf.0" ? Infinity : NaN) * (m[1] == "+" ? 1 : -1);
+      const v = (m[2] == "inf.0" ? Infinity : NaN) * (m[1] == "+" ? 1 : -1);
+      return [v, tok.substring(m[0].length)];
     }
 
+    // Handle the sign in case this is a number
+    let s = tok;
     let sign = 1;
-    if (this.match(/^\+/)) {      this.i++; asSym += "+"; }
-    else if (this.match(/^\-/)) { this.i++; asSym += "-"; sign = -1; }
+    if (s.match(/^\+/)) {      s = s.substring(1); }
+    else if (s.match(/^\-/)) { s = s.substring(1); sign = -1; }
+
+    // Handle deciaml of the form `.123`
+    if (base == 10 && s[0] == ".") {
+      const mm = tok.match(/^\.(\d+)([eE][+-]?\d+)?/)
+      if (mm) {
+        return [parseFloat("0"+mm[0]), tok.substring(mm[0].length)];
+      } else {
+        return [null, tok];
+      }
+    }
 
-    let a = null;
-    const mm = this.match(DIGITS[base]);
+    // Get the first number part
+    let a = 0;
+    const mm = s.match(DIGITS[base]);
     if (mm) {
-      this.i += mm[0].length;
-      asSym += mm[0];
+      s = s.substring(mm[0].length);
       a = parseInt(mm[0], base) * sign;
-    } else if (base == 10 && this.txt[this.i] == ".") {
-      // May be a decimal of the form `.123`. Continue parsing
-    } else if (maybeSymbol) {
-      // Continue parsing.
     } else {
-      throw new BiwaError("invalid char in number literal", this.rest());
+      return [null, tok];
     }
-
     // Parse rational
-    if (this.txt[this.i] == "/") {
-      this.i++;
-      const mmm = this.match(DIGITS[base]);
+    if (s[0] == "/") {
+      s = s.substring(1);
+      const mmm = s.match(DIGITS[base]);
       if (mmm) {
-        this.i += mmm[0].length;
+        s = s.substring(mmm[0].length);
         const b = parseInt(mmm[0], base);
-        return new Rational(a, b);
-      } else if (maybeSymbol) {
-        asSym += "/";
+        return [new Rational(a, b), s];
       } else {
-        throw new BiwaError("invalid char in rational number literal", this.rest());
+        return [null, tok];
       }
     }
 
     // Was not a rational. Check if it is a decimal
     if (base == 10) {
       // Try matching form the beginning
-      this.i -= asSym.length;
-      const mmm = this.match(/^[+-]?(\d+\.\d*|\.?\d+)([eE][+-]?\d+)?/)
+      const mmm = tok.match(/^[+-]?(\d+\.\d*|\.?\d+)([eE][+-]?\d+)?/)
       if (mmm) {
-        this.i += mmm[0].length;
-        return parseFloat(mmm[0]);
-      } else {
-        // Was not a decimal either. Put back the cursor
-        this.i += asSym.length;
+        return [parseFloat(mmm[0]), tok.substring(mmm[0].length)];
       }
     }
 
-    if (maybeSymbol) {
-      return asSym;
-    } else {
-      throw new BiwaError(`invalid chars in number literal (${asSym})`, this.rest());
-    }
+    return [null, tok];
   }
 
   // Parse a datum label definition (`#0=`) or reference (`#0#`).
-  _parseDatumLabel() {
-    const m = this.match(/^(\d+)(=|#)/);
+  _parseDatumLabel(tok) {
+    const m = tok.match(/^#(\d+)(=|#)$/);
     if (m) {
-      this.i += m[0].length;
       const id = parseInt(m[1]);
       if (m[2] == "=") {
+        // Definition
         const v = this._getObjectOrThrowUnterminated("got EOS for labelled datum");
         this.labelledData[id] = v;
         return v;
       } else {
+        // Reference
         if (this.labelledData.hasOwnProperty(id)) {
           return this.labelledData[id];
         } else {
-          throw new BiwaError("undefined datum reference", this.rest(-1));
+          throw new BiwaError("undefined datum reference", tok);
         }
       }
     } else {
-      throw new BiwaError("unknown #-syntax", this.rest(-1));
+      throw new BiwaError("[BUG] not a datum label", tok);
     }
   }
 
@@ -493,9 +564,9 @@ class Parser {
     throw new Unterminated("found EOS in list", this.rest());
   }
 
-  // Parse a string literal (`"..."`)
+  // Parse a string literal after the first `"`
   _parseString() {
-    const m = this.match(/^"((\\"|[^"])*)"/);
+    const m = this.match(/^((\\"|[^"])*)"/);
     if (m) {
       this.i += m[0].length;
       const s = m[1].replaceAll(/\\\s*\n\s*/g, "");
@@ -505,9 +576,9 @@ class Parser {
     }
   }
 
-  // Parse a symbol enclosed with `|`
+  // Parse a symbol enclosed with `|` after the first `|`
   _parseEnclosedSymbol() {
-    const m = this.match(/^\|((\\\||[^\|])*)\|/);
+    const m = this.match(/^((\\\||[^\|])*)\|/);
     if (m) {
       this.i += m[0].length;
       return Sym(this._replaceEscapedChars(m[1]));
@@ -524,23 +595,12 @@ class Parser {
   }
 
   // Parse a number or an identifier.
-  _parseDecimalNumberOrIdentifier() {
-    const c = this.txt[this.i];
-    if (c == "#") throw new Bug("#-syntax must be parsed beforehand")
-    if (c === undefined) throw new Bug("EOS must be handled beforehand")
+  _parseDecimalNumberOrIdentifier(tok) {
+    if (tok === "") throw new Bug("empty string given")
 
-    let v = this._parseRealNumber(10, true);
-    if (isString(v)) {
-      // Read the rest of this identifier
-      const m = this.match(/^[^\s)}\]]+/);
-      if (m) {
-        this.i += m[0].length;
-        v += m[0];
-      }
-      if (this.foldCase) {
-        v = v.toLowerCase();
-      }
-      return Sym(v);
+    const v = this._parseComplexNumber(10, tok);
+    if (v === null) {
+      return Sym(this.foldCase ? tok.toLowerCase() : tok);
     } else {
       return v;
     }


Test patch:
diff --git a/test/system/parser.test.js b/test/system/parser.test.js
index 32d0364..dc89fc9 100644
--- a/test/system/parser.test.js
+++ b/test/system/parser.test.js
@@ -1,5 +1,9 @@
 import { nil } from "../../src/header.js"
-import { Pair } from "../../src/system/pair.js"
+import { Pair, List } from "../../src/system/pair.js"
+import { Sym } from "../../src/system/symbol.js"
+import { Char } from "../../src/system/char.js"
+import { Complex, Rational } from "../../src/system/number.js"
+import { to_write } from "../../src/system/_writer.js"
 import Parser from "../../src/system/parser.js"
 
 function parse(txt) {
@@ -7,10 +11,6 @@ function parse(txt) {
 }
 
 describe("Parser (errneous)", () => {
-  test("Unterminated list", () => {
-    expect(() => parse("(1 2")).toThrow(Parser.Unterminated);
-  })
-
   test("Extra closing paren", () => {
     const p = new Parser("(display 10))");
     expect(p.getObject()).toBeInstanceOf(Pair);
@@ -23,7 +23,192 @@ describe("Parser", () => {
     expect(parse("")).toBe(Parser.EOS);
   })
 
+  describe("atmosphere", () => {
+    test("tab", () => {
+      expect(parse("#(1\t2)")).toEqual([1, 2]);
+    })
+
+    test("line comment", () => {
+      expect(parse("#(1; comment\n 2)")).toEqual([1, 2]);
+    })
+
+    test("block comment", () => {
+      expect(parse("#(1 #| comment |# 2)")).toEqual([1, 2]);
+    })
+
+    test("sexp comment", () => {
+      expect(parse("#(1 #;() 2)")).toEqual([1, 2]);
+    })
+  })
+
+  describe("case directive", () => {
+    test("fold to no-fold", () => {
+      expect(parse("#( #!fold-case A #!no-fold-case B)")).toEqual([Sym("a"), Sym("B")]);
+    })
+
+    test("no-fold to fold", () => {
+      expect(parse("#( #!no-fold-case A #!fold-case B)")).toEqual([Sym("A"), Sym("b")]);
+    })
+  })
+
   test("nil", () => {
     expect(parse("()")).toBe(nil);
   })
+
+  test("boolean", () => {
+    expect(parse("#(#t #true #f #false)")).toEqual([true, true, false, false]);
+  })
+
+  describe("numbers", () => {
+    test("integer", () => {
+      expect(parse("1")).toBe(1);
+    })
+
+    test("decimal", () => {
+      expect(parse("1.2")).toBe(1.2);
+      expect(parse(".2")).toBe(0.2);
+    })
+
+    test("rational", () => {
+      expect(parse("1/2")).toEqual(new Rational(1, 2));
+    })
+
+    test("polar complex", () => {
+      expect(parse("1@2")).toEqual(Complex.from_polar(1, 2));
+    })
+
+    test("ortho complex", () => {
+      expect(parse("-1+2i")).toEqual(new Complex(-1, 2));
+    })
+
+    test("rational in complex", () => {
+      expect(parse("1/2+3/4i")).toEqual(new Complex(new Rational(1, 2), new Rational(3, 4)));
+    })
+
+    test("decimal in complex", () => {
+      expect(parse(".2+2.3i")).toEqual(new Complex(0.2, 2.3));
+    })
+  })
+
+  describe("symbols", () => {
+    test("simple", () => {
+      expect(parse("foo")).toBe(Sym("foo"));
+    })
+
+    test("starts with integer", () => {
+      expect(parse("1+")).toBe(Sym("1+"));
+    })
+
+    test("starts with decimal", () => {
+      expect(parse(".1+")).toBe(Sym(".1+"));
+    })
+
+    test("starts with rational", () => {
+      expect(parse("1/2+")).toBe(Sym("1/2+"));
+    })
+
+    test("whitespace", () => {
+      expect(parse('| |')).toBe(Sym(' '));
+    })
+
+    test("newline", () => {
+      expect(parse('|a\nb|')).toBe(Sym('a\nb'));
+    })
+  })
+
+  describe("strings", () => {
+    test("simple", () => {
+      expect(parse('"hello"')).toBe("hello");
+    })
+
+    test("whitespace", () => {
+      expect(parse('"hello world"')).toBe("hello world");
+    })
+
+    test("tab", () => {
+      expect(parse('"hello\tworld"')).toBe("hello\tworld");
+    })
+
+    test("newline", () => {
+      expect(parse('"hello\nworld"')).toBe("hello\nworld");
+    })
+  })
+
+  describe("chars", () => {
+    test("simple", () => {
+      expect(parse("#\\a")).toBe(Char.get("a"));
+    })
+
+    test("number", () => {
+      expect(parse("#\\1")).toBe(Char.get("1"));
+    })
+
+    test("paren", () => {
+      expect(parse("#\\(")).toBe(Char.get("("));
+    })
+
+    test("backslash", () => {
+      expect(parse("#\\\\")).toBe(Char.get("\\"));
+    })
+
+    test("whitespace", () => {
+      expect(parse("#\\ ")).toBe(Char.get(" "));
+    })
+
+    test("tab", () => {
+      expect(parse("#\\\t")).toBe(Char.get("\t"));
+    })
+
+    test("newline", () => {
+      expect(parse("#\\\n")).toBe(Char.get("\n"));
+    })
+
+    test("named", () => {
+      expect(parse("#\\newline")).toBe(Char.get("\n"));
+    })
+
+    test("unknown name", () => {
+      expect(() => parse("#\\foo")).toThrow(Parser.Invalid)
+    })
+  })
+
+  describe("lists", () => {
+    test("number list", () => {
+      expect(to_write(parse("(1 2)"))).toBe("(1 2)");
+    })
+
+    test("Unterminated list", () => {
+      expect(() => parse("(1 2")).toThrow(Parser.Unterminated);
+    })
+  })
+
+  test("vector", () => {
+    expect(parse("#(1 2)")).toEqual([1, 2]);
+  })
+
+  describe("quotes", () => {
+    test("quoted symbol", () => {
+      expect(to_write(parse("'foo"))).toBe("(quote foo)");
+    })
+
+    test("quoted number", () => {
+      expect(to_write(parse("'1"))).toBe("(quote 1)");
+    })
+
+    test("double quoted", () => {
+      expect(to_write(parse("''foo"))).toBe("(quote (quote foo))");
+    })
+
+    test("unterminated quote", () => {
+      expect(() => parse("''")).toThrow(Parser.Unterminated);
+    })
+
+    test("quasiquote", () => {
+      expect(to_write(parse("`(,foo ,@bar)"))).toBe("(quasiquote ((unquote foo) (unquote-splicing bar)))");
+    })
+  })
+
+  test("datum label", () => {
+    expect(parse("#(#0=123 #0#)")).toEqual([123, 123]);
+  })
 })