Compare commits
60 Commits
20531d1154
...
arghing
Author | SHA1 | Date | |
---|---|---|---|
3de96b813f
|
|||
a73acedc6d
|
|||
92e3d6fe5b
|
|||
395006cdb8
|
|||
622d47071a
|
|||
1452d544bb
|
|||
56c6a8cf09
|
|||
cc29386cee
|
|||
95453bf197
|
|||
a6526af6ff
|
|||
3ea30a997a
|
|||
03edacc8ec
|
|||
d1ffbe25a3
|
|||
dc3a40b19d
|
|||
f2e0de1b66
|
|||
58842504c4
|
|||
1080737931
|
|||
de6e907c60
|
|||
9989801e62
|
|||
8c280c303e
|
|||
b2e61cd0d2
|
|||
c15bafe55d
|
|||
af7d5c6e14
|
|||
512eddc1ab
|
|||
2bc9788441
|
|||
c52d9b9563
|
|||
fbb04df86d
|
|||
e30cbe2aca
|
|||
116ad347db
|
|||
1493deac96
|
|||
a7e45b8add
|
|||
f1cc614836
|
|||
a2661f4369
|
|||
b020618d57
|
|||
d2405f75d8
|
|||
1d827b517b
|
|||
47d658f04f
|
|||
6c45a1390b
|
|||
b25e99a632
|
|||
abd957d42f
|
|||
d326a055f7
|
|||
6d6e600720
|
|||
d2563484f9
|
|||
eac3c2d8a6
|
|||
052fb87667
|
|||
9f431fcb11
|
|||
7e6a2ea52b
|
|||
f45663f248
|
|||
951c3bcebe
|
|||
f57429613a
|
|||
7e976ccc6f
|
|||
278ec88944
|
|||
f40981544c
|
|||
cbcf9ce5fc
|
|||
30ef5d8c91
|
|||
5d0811636f
|
|||
90f6fdad97
|
|||
c57baae2cc
|
|||
013a8faa2f
|
|||
798575d9ef
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
|||||||
*.hex
|
*.hex
|
||||||
*.log
|
*.log
|
||||||
|
*.out
|
||||||
*env
|
*env
|
||||||
.dep
|
.dep
|
||||||
**/target/
|
**/target/
|
||||||
|
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright (C) 2020 Dan Buch
|
Copyright (C) 2022 Dan Buch
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
@@ -1,13 +0,0 @@
|
|||||||
[[source]]
|
|
||||||
url = "https://pypi.org/simple"
|
|
||||||
verify_ssl = true
|
|
||||||
name = "pypi"
|
|
||||||
|
|
||||||
[packages]
|
|
||||||
|
|
||||||
[dev-packages]
|
|
||||||
mypy = ""
|
|
||||||
pytest = ""
|
|
||||||
|
|
||||||
[requires]
|
|
||||||
python_version = "3.9"
|
|
156
aoc2020/py/Pipfile.lock
generated
156
aoc2020/py/Pipfile.lock
generated
@@ -1,156 +0,0 @@
|
|||||||
{
|
|
||||||
"_meta": {
|
|
||||||
"hash": {
|
|
||||||
"sha256": "ed02d1728cc686824535903ae2f5f3956ba104434c4c6e532237df55bcd69a12"
|
|
||||||
},
|
|
||||||
"pipfile-spec": 6,
|
|
||||||
"requires": {
|
|
||||||
"python_version": "3.9"
|
|
||||||
},
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"name": "pypi",
|
|
||||||
"url": "https://pypi.org/simple",
|
|
||||||
"verify_ssl": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"default": {},
|
|
||||||
"develop": {
|
|
||||||
"attrs": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
|
|
||||||
"sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==20.3.0"
|
|
||||||
},
|
|
||||||
"iniconfig": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
|
|
||||||
"sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"
|
|
||||||
],
|
|
||||||
"version": "==1.1.1"
|
|
||||||
},
|
|
||||||
"mypy": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324",
|
|
||||||
"sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc",
|
|
||||||
"sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802",
|
|
||||||
"sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122",
|
|
||||||
"sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975",
|
|
||||||
"sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7",
|
|
||||||
"sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666",
|
|
||||||
"sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669",
|
|
||||||
"sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178",
|
|
||||||
"sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01",
|
|
||||||
"sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea",
|
|
||||||
"sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de",
|
|
||||||
"sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1",
|
|
||||||
"sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"markers": "python_version >= '3.5'",
|
|
||||||
"version": "==0.790"
|
|
||||||
},
|
|
||||||
"mypy-extensions": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
|
||||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
|
||||||
],
|
|
||||||
"version": "==0.4.3"
|
|
||||||
},
|
|
||||||
"packaging": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858",
|
|
||||||
"sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==20.8"
|
|
||||||
},
|
|
||||||
"pluggy": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
|
||||||
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==0.13.1"
|
|
||||||
},
|
|
||||||
"py": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
|
|
||||||
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==1.10.0"
|
|
||||||
},
|
|
||||||
"pyparsing": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
|
||||||
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==2.4.7"
|
|
||||||
},
|
|
||||||
"pytest": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:1969f797a1a0dbd8ccf0fecc80262312729afea9c17f1d70ebf85c5e76c6f7c8",
|
|
||||||
"sha256:66e419b1899bc27346cb2c993e12c5e5e8daba9073c1fbce33b9807abc95c306"
|
|
||||||
],
|
|
||||||
"index": "pypi",
|
|
||||||
"markers": "python_version >= '3.6'",
|
|
||||||
"version": "==6.2.1"
|
|
||||||
},
|
|
||||||
"toml": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
|
||||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
|
||||||
],
|
|
||||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
|
||||||
"version": "==0.10.2"
|
|
||||||
},
|
|
||||||
"typed-ast": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1",
|
|
||||||
"sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d",
|
|
||||||
"sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6",
|
|
||||||
"sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd",
|
|
||||||
"sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37",
|
|
||||||
"sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151",
|
|
||||||
"sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07",
|
|
||||||
"sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440",
|
|
||||||
"sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70",
|
|
||||||
"sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496",
|
|
||||||
"sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea",
|
|
||||||
"sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400",
|
|
||||||
"sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc",
|
|
||||||
"sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606",
|
|
||||||
"sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc",
|
|
||||||
"sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581",
|
|
||||||
"sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412",
|
|
||||||
"sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a",
|
|
||||||
"sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2",
|
|
||||||
"sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787",
|
|
||||||
"sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f",
|
|
||||||
"sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937",
|
|
||||||
"sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64",
|
|
||||||
"sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487",
|
|
||||||
"sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b",
|
|
||||||
"sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41",
|
|
||||||
"sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a",
|
|
||||||
"sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3",
|
|
||||||
"sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166",
|
|
||||||
"sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10"
|
|
||||||
],
|
|
||||||
"version": "==1.4.2"
|
|
||||||
},
|
|
||||||
"typing-extensions": {
|
|
||||||
"hashes": [
|
|
||||||
"sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
|
|
||||||
"sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
|
|
||||||
"sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
|
|
||||||
],
|
|
||||||
"version": "==3.7.4.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,49 +0,0 @@
|
|||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
inputs = [int(s) for s in sys.stdin.read().split()]
|
|
||||||
for i, j in _find_2020_pairs(inputs):
|
|
||||||
print(f"pair: {i} * {j} == {i * j}")
|
|
||||||
|
|
||||||
for i, j, k in _find_2020_triplets(inputs):
|
|
||||||
print(f"triplet: {i} * {j} * {k} == {i * j * k}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def _find_2020_pairs(
|
|
||||||
inputs: typing.List[int],
|
|
||||||
) -> typing.Generator[typing.Tuple[int, ...], None, None]:
|
|
||||||
found = []
|
|
||||||
|
|
||||||
for i, in0 in enumerate(inputs):
|
|
||||||
for j, in1 in enumerate(inputs):
|
|
||||||
if i == j:
|
|
||||||
continue
|
|
||||||
if in0 + in1 == 2020:
|
|
||||||
to_yield = tuple(sorted([in0, in1]))
|
|
||||||
if to_yield not in found:
|
|
||||||
yield to_yield
|
|
||||||
found.append(to_yield)
|
|
||||||
|
|
||||||
|
|
||||||
def _find_2020_triplets(
|
|
||||||
inputs: typing.List[int],
|
|
||||||
) -> typing.Generator[typing.Tuple[int, ...], None, None]:
|
|
||||||
found = []
|
|
||||||
|
|
||||||
for i, in0 in enumerate(inputs):
|
|
||||||
for j, in1 in enumerate(inputs):
|
|
||||||
for k, in2 in enumerate(inputs):
|
|
||||||
if i == j or i == k:
|
|
||||||
continue
|
|
||||||
if in0 + in1 + in2 == 2020:
|
|
||||||
to_yield = tuple(sorted([in0, in1, in2]))
|
|
||||||
if to_yield not in found:
|
|
||||||
yield to_yield
|
|
||||||
found.append(to_yield)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,53 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
n_valid = 0
|
|
||||||
total = 0
|
|
||||||
|
|
||||||
for pol_pas in [PolicyPassword.fromstring(s) for s in sys.stdin.readlines(False)]:
|
|
||||||
if pol_pas.is_valid():
|
|
||||||
n_valid += 1
|
|
||||||
total += 1
|
|
||||||
|
|
||||||
print(f"{n_valid}/{total} valid")
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
class Policy:
|
|
||||||
def __init__(self, char: str, pos1: int, pos2: int):
|
|
||||||
self.char = char
|
|
||||||
self.pos1 = pos1
|
|
||||||
self.pos2 = pos2
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fromstring(cls, input_string) -> "Policy":
|
|
||||||
parts = [s.strip() for s in input_string.split(" ")][:2]
|
|
||||||
pos = [int(s) for s in parts[0].split("-")][:2]
|
|
||||||
return cls(parts[1], pos[0], pos[1])
|
|
||||||
|
|
||||||
def is_valid(self, password: str) -> bool:
|
|
||||||
matches = 0
|
|
||||||
for pos in (self.pos1 - 1, self.pos2 - 1):
|
|
||||||
if password[pos] == self.char:
|
|
||||||
matches += 1
|
|
||||||
return matches == 1
|
|
||||||
|
|
||||||
|
|
||||||
class PolicyPassword:
|
|
||||||
def __init__(self, policy: "Policy", password: str):
|
|
||||||
self.policy = policy
|
|
||||||
self.password = password
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def fromstring(cls, input_string: str) -> "PolicyPassword":
|
|
||||||
parts = [s.strip() for s in input_string.split(":")][:2]
|
|
||||||
return cls(Policy.fromstring(parts[0]), parts[1])
|
|
||||||
|
|
||||||
def is_valid(self) -> bool:
|
|
||||||
return self.policy.is_valid(self.password)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,52 +0,0 @@
|
|||||||
import functools
|
|
||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
class Loc(typing.NamedTuple):
|
|
||||||
x: int
|
|
||||||
y: int
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
forest_frame = [list(line.strip()) for line in sys.stdin.readlines()]
|
|
||||||
frame_width = len(forest_frame[0])
|
|
||||||
frame_height = len(forest_frame)
|
|
||||||
|
|
||||||
all_trees_encountered = []
|
|
||||||
|
|
||||||
for slope in [
|
|
||||||
Loc(x=1, y=1),
|
|
||||||
Loc(x=3, y=1),
|
|
||||||
Loc(x=5, y=1),
|
|
||||||
Loc(x=7, y=1),
|
|
||||||
Loc(x=1, y=2),
|
|
||||||
]:
|
|
||||||
loc = Loc(x=0, y=0)
|
|
||||||
|
|
||||||
trees_encountered = 0
|
|
||||||
|
|
||||||
while loc.y <= (frame_height - 1):
|
|
||||||
at_loc = forest_frame[loc.y][loc.x]
|
|
||||||
if at_loc == "#":
|
|
||||||
trees_encountered += 1
|
|
||||||
|
|
||||||
next_x = (loc.x + slope.x) % frame_width
|
|
||||||
next_y = loc.y + slope.y
|
|
||||||
next_loc = Loc(x=next_x, y=next_y)
|
|
||||||
loc = next_loc
|
|
||||||
|
|
||||||
print(
|
|
||||||
f"(slope right={slope.x} down={slope.y}) trees encountered: {trees_encountered}"
|
|
||||||
)
|
|
||||||
all_trees_encountered.append(trees_encountered)
|
|
||||||
|
|
||||||
trees_encountered_product = functools.reduce(
|
|
||||||
lambda x, y: x * y, all_trees_encountered
|
|
||||||
)
|
|
||||||
print(f"trees encountered product: {trees_encountered_product}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,115 +0,0 @@
|
|||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
checked = []
|
|
||||||
for passport in _read_passports(sys.stdin):
|
|
||||||
if passport is None:
|
|
||||||
checked.append(False)
|
|
||||||
continue
|
|
||||||
checked.append(passport.is_valid())
|
|
||||||
|
|
||||||
print(f"total={len(checked)} valid={checked.count(True)}")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
NoneString = typing.Optional[str]
|
|
||||||
|
|
||||||
VALID_EYE_COLORS = ("amb", "blu", "brn", "gry", "grn", "hzl", "oth")
|
|
||||||
|
|
||||||
|
|
||||||
class Passport:
|
|
||||||
byr: NoneString = None
|
|
||||||
cid: NoneString = None
|
|
||||||
ecl: NoneString = None
|
|
||||||
eyr: NoneString = None
|
|
||||||
hcl: NoneString = None
|
|
||||||
hgt: NoneString = None
|
|
||||||
iyr: NoneString = None
|
|
||||||
pid: NoneString = None
|
|
||||||
|
|
||||||
def is_valid(self) -> bool:
|
|
||||||
return (
|
|
||||||
self._is_year_in_range(self.byr, range(1920, 2003))
|
|
||||||
and self._is_year_in_range(self.iyr, range(2010, 2021))
|
|
||||||
and self._is_year_in_range(self.eyr, range(2020, 2031))
|
|
||||||
and self._has_valid_height()
|
|
||||||
and self._has_valid_hair_color()
|
|
||||||
and self.ecl in VALID_EYE_COLORS
|
|
||||||
and self._has_valid_passport_id()
|
|
||||||
)
|
|
||||||
|
|
||||||
def _has_valid_height(self) -> bool:
|
|
||||||
if self.hgt is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
height_value = 0
|
|
||||||
|
|
||||||
height_value_string = self.hgt.replace("cm", "").replace("in", "")
|
|
||||||
if not height_value_string.isdigit():
|
|
||||||
return False
|
|
||||||
|
|
||||||
height = int(height_value_string)
|
|
||||||
|
|
||||||
if self.hgt.endswith("cm") and height in range(150, 194):
|
|
||||||
return True
|
|
||||||
elif self.hgt.endswith("in") and height in range(59, 77):
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _has_valid_hair_color(self) -> bool:
|
|
||||||
if self.hcl is None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not self.hcl.startswith("#"):
|
|
||||||
return False
|
|
||||||
|
|
||||||
hair_value = self.hcl.replace("#", "")
|
|
||||||
if len(hair_value) != 6:
|
|
||||||
return False
|
|
||||||
|
|
||||||
try:
|
|
||||||
_ = int(hair_value, 16)
|
|
||||||
return True
|
|
||||||
except ValueError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _has_valid_passport_id(self) -> bool:
|
|
||||||
return (
|
|
||||||
self.pid is not None
|
|
||||||
and len(self.pid) == 9
|
|
||||||
and all([s.isdigit() for s in list(self.pid)])
|
|
||||||
)
|
|
||||||
|
|
||||||
def _is_year_in_range(self, value: NoneString, yr_range: range) -> bool:
|
|
||||||
if value is None:
|
|
||||||
return False
|
|
||||||
if len(value) != 4:
|
|
||||||
return False
|
|
||||||
if not value.isdigit():
|
|
||||||
return False
|
|
||||||
return int(value) in yr_range
|
|
||||||
|
|
||||||
|
|
||||||
def _read_passports(
|
|
||||||
instream: typing.TextIO,
|
|
||||||
) -> typing.Generator[typing.Optional[Passport], None, None]:
|
|
||||||
cur = Passport()
|
|
||||||
|
|
||||||
for i, line in enumerate(instream):
|
|
||||||
line = line.strip()
|
|
||||||
if line == "":
|
|
||||||
yield cur
|
|
||||||
cur = Passport()
|
|
||||||
|
|
||||||
for pair in line.split():
|
|
||||||
attr, value = pair.split(":", 1)
|
|
||||||
setattr(cur, attr, value)
|
|
||||||
|
|
||||||
yield cur
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,77 +0,0 @@
|
|||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
highest_seat = 0
|
|
||||||
|
|
||||||
taken_seats = set()
|
|
||||||
|
|
||||||
for line in sys.stdin:
|
|
||||||
line = line.strip()
|
|
||||||
if line == "":
|
|
||||||
continue
|
|
||||||
|
|
||||||
seat = _locate_seat(line)
|
|
||||||
|
|
||||||
if seat.number > highest_seat:
|
|
||||||
highest_seat = seat.number
|
|
||||||
|
|
||||||
taken_seats.add(seat.number)
|
|
||||||
|
|
||||||
print(f"bp={line} row={seat.row} column={seat.col} seat={seat.number}")
|
|
||||||
|
|
||||||
seat_number: typing.Optional[int] = None
|
|
||||||
n_found = 0
|
|
||||||
|
|
||||||
for candidate_seat in range(0, (127 * 8) + 1):
|
|
||||||
if candidate_seat in taken_seats:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if (candidate_seat - 1) in taken_seats and (candidate_seat + 1) in taken_seats:
|
|
||||||
seat_number = candidate_seat
|
|
||||||
n_found += 1
|
|
||||||
|
|
||||||
print(f"highest_seat={highest_seat} seat_number={seat_number} n_found={n_found}")
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
class Seat:
|
|
||||||
row: int
|
|
||||||
column: int
|
|
||||||
|
|
||||||
def __init__(self, row: int = 0, col: int = 0):
|
|
||||||
self.row = row
|
|
||||||
self.col = col
|
|
||||||
|
|
||||||
@property
|
|
||||||
def number(self) -> int:
|
|
||||||
return (self.row * 8) + self.col
|
|
||||||
|
|
||||||
|
|
||||||
def _locate_seat(bp: str) -> Seat:
|
|
||||||
rows = list(range(0, 128))
|
|
||||||
cols = list(range(0, 8))
|
|
||||||
|
|
||||||
row_part = list(bp[:7])
|
|
||||||
col_part = list(bp[7:])
|
|
||||||
|
|
||||||
return Seat(
|
|
||||||
row=_bisect(rows, [{"F": 0, "B": 1}[s] for s in row_part]),
|
|
||||||
col=_bisect(cols, [{"L": 0, "R": 1}[s] for s in col_part]),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _bisect(initial_selection: typing.List[int], bisections: typing.List[int]) -> int:
|
|
||||||
selection = initial_selection[:]
|
|
||||||
|
|
||||||
for bisection in bisections:
|
|
||||||
halfway = int(len(selection) / 2)
|
|
||||||
selection = [selection[:halfway], selection[halfway:]][bisection]
|
|
||||||
|
|
||||||
return selection[0]
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,3 +0,0 @@
|
|||||||
BFFFBBFRRR
|
|
||||||
FFFBBBFRRR
|
|
||||||
BBFFBBFRLL
|
|
@@ -1,3 +0,0 @@
|
|||||||
bp=BFFFBBFRRR row=70 column=7 seat=567
|
|
||||||
bp=FFFBBBFRRR row=14 column=7 seat=119
|
|
||||||
bp=BBFFBBFRLL row=102 column=4 seat=820
|
|
@@ -1,21 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from solution import main
|
|
||||||
|
|
||||||
|
|
||||||
HERE = Path(__file__).absolute().parent
|
|
||||||
|
|
||||||
|
|
||||||
def test_solution(capsys):
|
|
||||||
with (HERE / "test-input").open() as infile:
|
|
||||||
sys.stdin = infile
|
|
||||||
main()
|
|
||||||
|
|
||||||
expected_output = (HERE / "test-output").read_text().splitlines()
|
|
||||||
assert expected_output == [
|
|
||||||
l for l in capsys.readouterr().out.splitlines() if l.startswith("counts_sum=")
|
|
||||||
]
|
|
@@ -1,42 +0,0 @@
|
|||||||
import sys
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
counts_sum = sum([c for c in _iter_group_counts(sys.stdin)])
|
|
||||||
print(f"counts_sum={counts_sum}")
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def _iter_group_counts(instream: typing.TextIO) -> typing.Generator[int, None, None]:
|
|
||||||
for i, group in enumerate(_iter_groups(instream)):
|
|
||||||
answers = set(list(group[0]))
|
|
||||||
print(f"i={i} initial={answers}")
|
|
||||||
|
|
||||||
for answers_text in group[1:]:
|
|
||||||
to_add = set(list(answers_text))
|
|
||||||
answers = answers.intersection(set(list(answers_text)))
|
|
||||||
print(f"i={i} added={to_add} result={answers}")
|
|
||||||
|
|
||||||
print(f"i={i} final={answers} n={len(answers)}")
|
|
||||||
yield len(answers)
|
|
||||||
|
|
||||||
|
|
||||||
def _iter_groups(instream):
|
|
||||||
cur_group = []
|
|
||||||
|
|
||||||
for line in instream:
|
|
||||||
line = line.strip()
|
|
||||||
if line == "":
|
|
||||||
yield cur_group
|
|
||||||
cur_group = []
|
|
||||||
continue
|
|
||||||
|
|
||||||
cur_group.append(line)
|
|
||||||
|
|
||||||
yield cur_group
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
@@ -1,15 +0,0 @@
|
|||||||
abc
|
|
||||||
|
|
||||||
a
|
|
||||||
b
|
|
||||||
c
|
|
||||||
|
|
||||||
ab
|
|
||||||
ac
|
|
||||||
|
|
||||||
a
|
|
||||||
a
|
|
||||||
a
|
|
||||||
a
|
|
||||||
|
|
||||||
b
|
|
@@ -1 +0,0 @@
|
|||||||
counts_sum=6
|
|
@@ -1,21 +0,0 @@
|
|||||||
import sys
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from solution import main
|
|
||||||
|
|
||||||
|
|
||||||
HERE = Path(__file__).absolute().parent
|
|
||||||
|
|
||||||
|
|
||||||
def test_solution(capsys):
|
|
||||||
with (HERE / "test-input").open() as infile:
|
|
||||||
sys.stdin = infile
|
|
||||||
main()
|
|
||||||
|
|
||||||
expected_output = (HERE / "test-output").read_text().splitlines()
|
|
||||||
assert expected_output == [
|
|
||||||
l for l in capsys.readouterr().out.splitlines() if l.startswith("counts_sum")
|
|
||||||
]
|
|
4
argh/README.md
Normal file
4
argh/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# argh command line parser
|
||||||
|
|
||||||
|
> NOTE: much of this is lifted from
|
||||||
|
> https://blog.gopheracademy.com/advent-2014/parsers-lexers/
|
34
argh/argh.go
Normal file
34
argh/argh.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package argh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
tracingEnabled = os.Getenv("ARGH_TRACING") == "enabled"
|
||||||
|
traceLogger *log.Logger
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if !tracingEnabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceLogger = log.New(os.Stderr, "ARGH TRACING: ", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func tracef(format string, v ...any) {
|
||||||
|
if !tracingEnabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, file, line, ok := runtime.Caller(1); ok {
|
||||||
|
format = fmt.Sprintf("%v:%v ", filepath.Base(file), line) + format
|
||||||
|
}
|
||||||
|
|
||||||
|
traceLogger.Printf(format, v...)
|
||||||
|
}
|
46
argh/cmd/argh-example/main.go
Normal file
46
argh/cmd/argh-example/main.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.meatballhat.com/x/box-o-sand/argh"
|
||||||
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
asJSON := os.Getenv("ARGH_OUTPUT_JSON") == "enabled"
|
||||||
|
|
||||||
|
log.SetFlags(0)
|
||||||
|
|
||||||
|
pt, err := argh.ParseArgs(os.Args, argh.NewParserConfig(
|
||||||
|
&argh.CommandConfig{
|
||||||
|
NValue: argh.OneOrMoreValue,
|
||||||
|
ValueNames: []string{"topping"},
|
||||||
|
Flags: &argh.Flags{
|
||||||
|
Automatic: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ast := argh.NewQuerier(pt.Nodes).AST()
|
||||||
|
|
||||||
|
if asJSON {
|
||||||
|
b, err := json.MarshalIndent(ast, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(b))
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
spew.Dump(ast)
|
||||||
|
}
|
12
argh/go.mod
Normal file
12
argh/go.mod
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
module git.meatballhat.com/x/box-o-sand/argh
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require github.com/pkg/errors v0.9.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/stretchr/testify v1.7.1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||||
|
)
|
12
argh/go.sum
Normal file
12
argh/go.sum
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
46
argh/node.go
Normal file
46
argh/node.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package argh
|
||||||
|
|
||||||
|
type Node interface{}
|
||||||
|
|
||||||
|
type TypedNode struct {
|
||||||
|
Type string
|
||||||
|
Node Node
|
||||||
|
}
|
||||||
|
|
||||||
|
type PassthroughArgs struct {
|
||||||
|
Nodes []Node
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompoundShortFlag struct {
|
||||||
|
Nodes []Node
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ident struct {
|
||||||
|
Literal string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BadArg struct {
|
||||||
|
Literal string
|
||||||
|
From Pos
|
||||||
|
To Pos
|
||||||
|
}
|
||||||
|
|
||||||
|
type Command struct {
|
||||||
|
Name string
|
||||||
|
Values map[string]string
|
||||||
|
Nodes []Node
|
||||||
|
}
|
||||||
|
|
||||||
|
type Flag struct {
|
||||||
|
Name string
|
||||||
|
Values map[string]string
|
||||||
|
Nodes []Node
|
||||||
|
}
|
||||||
|
|
||||||
|
type StdinFlag struct{}
|
||||||
|
|
||||||
|
type StopFlag struct{}
|
||||||
|
|
||||||
|
type ArgDelimiter struct{}
|
||||||
|
|
||||||
|
type Assign struct{}
|
26
argh/nvalue_string.go
Normal file
26
argh/nvalue_string.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
// Code generated by "stringer -type NValue"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package argh
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[OneOrMoreValue - -2]
|
||||||
|
_ = x[ZeroOrMoreValue - -1]
|
||||||
|
_ = x[ZeroValue-0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _NValue_name = "OneOrMoreValueZeroOrMoreValueZeroValue"
|
||||||
|
|
||||||
|
var _NValue_index = [...]uint8{0, 14, 29, 38}
|
||||||
|
|
||||||
|
func (i NValue) String() string {
|
||||||
|
i -= -2
|
||||||
|
if i < 0 || i >= NValue(len(_NValue_index)-1) {
|
||||||
|
return "NValue(" + strconv.FormatInt(int64(i+-2), 10) + ")"
|
||||||
|
}
|
||||||
|
return _NValue_name[_NValue_index[i]:_NValue_index[i+1]]
|
||||||
|
}
|
346
argh/parser.go
Normal file
346
argh/parser.go
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
package argh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type parser struct {
|
||||||
|
s *Scanner
|
||||||
|
|
||||||
|
cfg *ParserConfig
|
||||||
|
|
||||||
|
errors ParserErrorList
|
||||||
|
|
||||||
|
tok Token
|
||||||
|
lit string
|
||||||
|
pos Pos
|
||||||
|
|
||||||
|
buffered bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParseTree struct {
|
||||||
|
Nodes []Node `json:"nodes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseArgs(args []string, pCfg *ParserConfig) (*ParseTree, error) {
|
||||||
|
p := &parser{}
|
||||||
|
p.init(
|
||||||
|
strings.NewReader(strings.Join(args, string(nul))),
|
||||||
|
pCfg,
|
||||||
|
)
|
||||||
|
|
||||||
|
tracef("ParseArgs(...) parser=%+#v", p)
|
||||||
|
|
||||||
|
return p.parseArgs()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) addError(msg string) {
|
||||||
|
p.errors.Add(Position{Column: int(p.pos)}, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) init(r io.Reader, pCfg *ParserConfig) {
|
||||||
|
p.errors = ParserErrorList{}
|
||||||
|
|
||||||
|
if pCfg == nil {
|
||||||
|
pCfg = POSIXyParserConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
p.cfg = pCfg
|
||||||
|
|
||||||
|
p.s = NewScanner(r, pCfg.ScannerConfig)
|
||||||
|
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseArgs() (*ParseTree, error) {
|
||||||
|
if p.errors.Len() != 0 {
|
||||||
|
tracef("parseArgs() bailing due to initial error")
|
||||||
|
return nil, p.errors.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
tracef("parseArgs() parsing %q as program command; cfg=%+#v", p.lit, p.cfg.Prog)
|
||||||
|
prog := p.parseCommand(&p.cfg.Prog)
|
||||||
|
|
||||||
|
tracef("parseArgs() top level node is %T", prog)
|
||||||
|
|
||||||
|
nodes := []Node{prog}
|
||||||
|
if v := p.parsePassthrough(); v != nil {
|
||||||
|
tracef("parseArgs() appending passthrough argument %v", v)
|
||||||
|
nodes = append(nodes, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
tracef("parseArgs() returning ParseTree")
|
||||||
|
|
||||||
|
return &ParseTree{Nodes: nodes}, p.errors.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) next() {
|
||||||
|
tracef("next() before scan: %v %q %v", p.tok, p.lit, p.pos)
|
||||||
|
|
||||||
|
p.tok, p.lit, p.pos = p.s.Scan()
|
||||||
|
|
||||||
|
tracef("next() after scan: %v %q %v", p.tok, p.lit, p.pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseCommand(cCfg *CommandConfig) Node {
|
||||||
|
tracef("parseCommand(%+#v)", cCfg)
|
||||||
|
|
||||||
|
node := &Command{
|
||||||
|
Name: p.lit,
|
||||||
|
}
|
||||||
|
values := map[string]string{}
|
||||||
|
nodes := []Node{}
|
||||||
|
|
||||||
|
identIndex := 0
|
||||||
|
|
||||||
|
for i := 0; p.tok != EOL; i++ {
|
||||||
|
if !p.buffered {
|
||||||
|
tracef("parseCommand(...) buffered=false; scanning next")
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
p.buffered = false
|
||||||
|
|
||||||
|
tracef("parseCommand(...) for=%d values=%+#v", i, values)
|
||||||
|
tracef("parseCommand(...) for=%d nodes=%+#v", i, nodes)
|
||||||
|
tracef("parseCommand(...) for=%d tok=%s lit=%q pos=%v", i, p.tok, p.lit, p.pos)
|
||||||
|
|
||||||
|
tracef("parseCommand(...) cCfg=%+#v", cCfg)
|
||||||
|
|
||||||
|
if subCfg, ok := cCfg.GetCommandConfig(p.lit); ok {
|
||||||
|
subCommand := p.lit
|
||||||
|
|
||||||
|
nodes = append(nodes, p.parseCommand(&subCfg))
|
||||||
|
|
||||||
|
tracef("parseCommand(...) breaking after sub-command=%v", subCommand)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p.tok {
|
||||||
|
case ARG_DELIMITER:
|
||||||
|
tracef("parseCommand(...) handling %s", p.tok)
|
||||||
|
|
||||||
|
nodes = append(nodes, &ArgDelimiter{})
|
||||||
|
|
||||||
|
continue
|
||||||
|
case IDENT, STDIN_FLAG:
|
||||||
|
tracef("parseCommand(...) handling %s", p.tok)
|
||||||
|
|
||||||
|
if cCfg.NValue.Contains(identIndex) {
|
||||||
|
name := fmt.Sprintf("%d", identIndex)
|
||||||
|
|
||||||
|
tracef("parseCommand(...) checking for name of identIndex=%d", identIndex)
|
||||||
|
|
||||||
|
if len(cCfg.ValueNames) > identIndex {
|
||||||
|
name = cCfg.ValueNames[identIndex]
|
||||||
|
tracef("parseCommand(...) setting name=%s from config value names", name)
|
||||||
|
} else if len(cCfg.ValueNames) == 1 && (cCfg.NValue == OneOrMoreValue || cCfg.NValue == ZeroOrMoreValue) {
|
||||||
|
name = fmt.Sprintf("%s.%d", cCfg.ValueNames[0], identIndex)
|
||||||
|
tracef("parseCommand(...) setting name=%s from repeating value name", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
values[name] = p.lit
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.tok == STDIN_FLAG {
|
||||||
|
nodes = append(nodes, &StdinFlag{})
|
||||||
|
} else {
|
||||||
|
nodes = append(nodes, &Ident{Literal: p.lit})
|
||||||
|
}
|
||||||
|
|
||||||
|
identIndex++
|
||||||
|
case LONG_FLAG, SHORT_FLAG, COMPOUND_SHORT_FLAG:
|
||||||
|
tok := p.tok
|
||||||
|
|
||||||
|
flagNode := p.parseFlag(cCfg.Flags)
|
||||||
|
|
||||||
|
tracef("parseCommand(...) appending %s node=%+#v", tok, flagNode)
|
||||||
|
|
||||||
|
nodes = append(nodes, flagNode)
|
||||||
|
case ASSIGN:
|
||||||
|
tracef("parseCommand(...) error on bare %s", p.tok)
|
||||||
|
|
||||||
|
p.addError("invalid bare assignment")
|
||||||
|
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
tracef("parseCommand(...) breaking on %s", p.tok)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nodes) > 0 {
|
||||||
|
node.Nodes = nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(values) > 0 {
|
||||||
|
node.Values = values
|
||||||
|
}
|
||||||
|
|
||||||
|
tracef("parseCommand(...) returning node=%+#v", node)
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseIdent() Node {
|
||||||
|
node := &Ident{Literal: p.lit}
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseFlag(flags *Flags) Node {
|
||||||
|
switch p.tok {
|
||||||
|
case SHORT_FLAG:
|
||||||
|
tracef("parseFlag(...) parsing short flag with config=%+#v", flags)
|
||||||
|
return p.parseShortFlag(flags)
|
||||||
|
case LONG_FLAG:
|
||||||
|
tracef("parseFlag(...) parsing long flag with config=%+#v", flags)
|
||||||
|
return p.parseLongFlag(flags)
|
||||||
|
case COMPOUND_SHORT_FLAG:
|
||||||
|
tracef("parseFlag(...) parsing compound short flag with config=%+#v", flags)
|
||||||
|
return p.parseCompoundShortFlag(flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("token %v cannot be parsed as flag", p.tok))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseShortFlag(flags *Flags) Node {
|
||||||
|
node := &Flag{Name: string(p.lit[1])}
|
||||||
|
|
||||||
|
flCfg, ok := flags.Get(node.Name)
|
||||||
|
if !ok {
|
||||||
|
p.addError(fmt.Sprintf("unknown flag %q", node.Name))
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.parseConfiguredFlag(node, flCfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseLongFlag(flags *Flags) Node {
|
||||||
|
node := &Flag{Name: string(p.lit[2:])}
|
||||||
|
|
||||||
|
flCfg, ok := flags.Get(node.Name)
|
||||||
|
if !ok {
|
||||||
|
p.addError(fmt.Sprintf("unknown flag %q", node.Name))
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.parseConfiguredFlag(node, flCfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseCompoundShortFlag(flags *Flags) Node {
|
||||||
|
flagNodes := []Node{}
|
||||||
|
|
||||||
|
withoutFlagPrefix := p.lit[1:]
|
||||||
|
|
||||||
|
for i, r := range withoutFlagPrefix {
|
||||||
|
node := &Flag{Name: string(r)}
|
||||||
|
|
||||||
|
if i == len(withoutFlagPrefix)-1 {
|
||||||
|
flCfg, ok := flags.Get(node.Name)
|
||||||
|
if !ok {
|
||||||
|
p.addError(fmt.Sprintf("unknown flag %q", node.Name))
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
flagNodes = append(flagNodes, p.parseConfiguredFlag(node, flCfg))
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
flagNodes = append(flagNodes, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CompoundShortFlag{Nodes: flagNodes}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseConfiguredFlag(node *Flag, flCfg FlagConfig) Node {
|
||||||
|
values := map[string]string{}
|
||||||
|
nodes := []Node{}
|
||||||
|
|
||||||
|
identIndex := 0
|
||||||
|
|
||||||
|
for i := 0; p.tok != EOL; i++ {
|
||||||
|
if !flCfg.NValue.Contains(identIndex) {
|
||||||
|
tracef("parseConfiguredFlag(...) identIndex=%d exceeds expected=%v; breaking", identIndex, flCfg.NValue)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
p.next()
|
||||||
|
|
||||||
|
switch p.tok {
|
||||||
|
case ARG_DELIMITER:
|
||||||
|
nodes = append(nodes, &ArgDelimiter{})
|
||||||
|
|
||||||
|
continue
|
||||||
|
case ASSIGN:
|
||||||
|
nodes = append(nodes, &Assign{})
|
||||||
|
|
||||||
|
continue
|
||||||
|
case IDENT, STDIN_FLAG:
|
||||||
|
name := fmt.Sprintf("%d", identIndex)
|
||||||
|
|
||||||
|
tracef("parseConfiguredFlag(...) checking for name of identIndex=%d", identIndex)
|
||||||
|
|
||||||
|
if len(flCfg.ValueNames) > identIndex {
|
||||||
|
name = flCfg.ValueNames[identIndex]
|
||||||
|
tracef("parseConfiguredFlag(...) setting name=%s from config value names", name)
|
||||||
|
} else if len(flCfg.ValueNames) == 1 && (flCfg.NValue == OneOrMoreValue || flCfg.NValue == ZeroOrMoreValue) {
|
||||||
|
name = fmt.Sprintf("%s.%d", flCfg.ValueNames[0], identIndex)
|
||||||
|
tracef("parseConfiguredFlag(...) setting name=%s from repeating value name", name)
|
||||||
|
} else {
|
||||||
|
tracef("parseConfiguredFlag(...) setting name=%s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
values[name] = p.lit
|
||||||
|
|
||||||
|
if p.tok == STDIN_FLAG {
|
||||||
|
nodes = append(nodes, &StdinFlag{})
|
||||||
|
} else {
|
||||||
|
nodes = append(nodes, &Ident{Literal: p.lit})
|
||||||
|
}
|
||||||
|
|
||||||
|
identIndex++
|
||||||
|
default:
|
||||||
|
tracef("parseConfiguredFlag(...) breaking on %s %q %v; setting buffered=true", p.tok, p.lit, p.pos)
|
||||||
|
p.buffered = true
|
||||||
|
|
||||||
|
if len(nodes) > 0 {
|
||||||
|
node.Nodes = nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(values) > 0 {
|
||||||
|
node.Values = values
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nodes) > 0 {
|
||||||
|
node.Nodes = nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(values) > 0 {
|
||||||
|
node.Values = values
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parsePassthrough() Node {
|
||||||
|
nodes := []Node{}
|
||||||
|
|
||||||
|
for ; p.tok != EOL; p.next() {
|
||||||
|
nodes = append(nodes, &Ident{Literal: p.lit})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nodes) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &PassthroughArgs{Nodes: nodes}
|
||||||
|
}
|
146
argh/parser_config.go
Normal file
146
argh/parser_config.go
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
package argh
|
||||||
|
|
||||||
|
const (
|
||||||
|
OneOrMoreValue NValue = -2
|
||||||
|
ZeroOrMoreValue NValue = -1
|
||||||
|
ZeroValue NValue = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
POSIXyParserConfig = NewParserConfig(
|
||||||
|
nil,
|
||||||
|
POSIXyScannerConfig,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
type NValue int
|
||||||
|
|
||||||
|
func (nv NValue) Contains(i int) bool {
|
||||||
|
tracef("NValue.Contains(%v)", i)
|
||||||
|
|
||||||
|
if i < int(ZeroValue) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if nv == OneOrMoreValue || nv == ZeroOrMoreValue {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(nv) > i
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParserConfig struct {
|
||||||
|
Prog CommandConfig
|
||||||
|
|
||||||
|
ScannerConfig *ScannerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewParserConfig(prog *CommandConfig, sCfg *ScannerConfig) *ParserConfig {
|
||||||
|
if sCfg == nil {
|
||||||
|
sCfg = POSIXyScannerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if prog == nil {
|
||||||
|
prog = &CommandConfig{}
|
||||||
|
}
|
||||||
|
|
||||||
|
prog.init()
|
||||||
|
|
||||||
|
pCfg := &ParserConfig{
|
||||||
|
Prog: *prog,
|
||||||
|
ScannerConfig: sCfg,
|
||||||
|
}
|
||||||
|
|
||||||
|
return pCfg
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandConfig struct {
|
||||||
|
NValue NValue
|
||||||
|
ValueNames []string
|
||||||
|
Flags *Flags
|
||||||
|
Commands *Commands
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cCfg *CommandConfig) init() {
|
||||||
|
if cCfg.ValueNames == nil {
|
||||||
|
cCfg.ValueNames = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cCfg.Flags == nil {
|
||||||
|
cCfg.Flags = &Flags{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cCfg.Commands == nil {
|
||||||
|
cCfg.Commands = &Commands{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cCfg *CommandConfig) GetCommandConfig(name string) (CommandConfig, bool) {
|
||||||
|
tracef("CommandConfig.GetCommandConfig(%q)", name)
|
||||||
|
|
||||||
|
if cCfg.Commands == nil {
|
||||||
|
cCfg.Commands = &Commands{Map: map[string]CommandConfig{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cCfg.Commands.Get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cCfg *CommandConfig) GetFlagConfig(name string) (FlagConfig, bool) {
|
||||||
|
tracef("CommandConfig.GetFlagConfig(%q)", name)
|
||||||
|
|
||||||
|
if cCfg.Flags == nil {
|
||||||
|
cCfg.Flags = &Flags{Map: map[string]FlagConfig{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cCfg.Flags.Get(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlagConfig struct {
|
||||||
|
NValue NValue
|
||||||
|
Persist bool
|
||||||
|
ValueNames []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Flags struct {
|
||||||
|
Parent *Flags
|
||||||
|
Map map[string]FlagConfig
|
||||||
|
|
||||||
|
Automatic bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fl *Flags) Get(name string) (FlagConfig, bool) {
|
||||||
|
tracef("Flags.Get(%q)", name)
|
||||||
|
|
||||||
|
if fl.Map == nil {
|
||||||
|
fl.Map = map[string]FlagConfig{}
|
||||||
|
}
|
||||||
|
|
||||||
|
flCfg, ok := fl.Map[name]
|
||||||
|
if !ok {
|
||||||
|
if fl.Automatic {
|
||||||
|
return FlagConfig{}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
if fl.Parent != nil {
|
||||||
|
flCfg, ok = fl.Parent.Get(name)
|
||||||
|
return flCfg, ok && flCfg.Persist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flCfg, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
type Commands struct {
|
||||||
|
Map map[string]CommandConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *Commands) Get(name string) (CommandConfig, bool) {
|
||||||
|
tracef("Commands.Get(%q)", name)
|
||||||
|
|
||||||
|
if cmd.Map == nil {
|
||||||
|
cmd.Map = map[string]CommandConfig{}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdCfg, ok := cmd.Map[name]
|
||||||
|
return cmdCfg, ok
|
||||||
|
}
|
88
argh/parser_error.go
Normal file
88
argh/parser_error.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package argh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParserError is largely borrowed from go/scanner.Error
|
||||||
|
type ParserError struct {
|
||||||
|
Pos Position
|
||||||
|
Msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ParserError) Error() string {
|
||||||
|
if e.Pos.IsValid() {
|
||||||
|
return e.Pos.String() + ":" + e.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParserErrorList is largely borrowed from go/scanner.ErrorList
|
||||||
|
type ParserErrorList []*ParserError
|
||||||
|
|
||||||
|
func (el *ParserErrorList) Add(pos Position, msg string) {
|
||||||
|
*el = append(*el, &ParserError{Pos: pos, Msg: msg})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el *ParserErrorList) Reset() { *el = (*el)[0:0] }
|
||||||
|
|
||||||
|
func (el ParserErrorList) Len() int { return len(el) }
|
||||||
|
|
||||||
|
func (el ParserErrorList) Swap(i, j int) { el[i], el[j] = el[j], el[i] }
|
||||||
|
|
||||||
|
func (el ParserErrorList) Less(i, j int) bool {
|
||||||
|
e := &el[i].Pos
|
||||||
|
f := &el[j].Pos
|
||||||
|
|
||||||
|
if e.Column != f.Column {
|
||||||
|
return e.Column < f.Column
|
||||||
|
}
|
||||||
|
|
||||||
|
return el[i].Msg < el[j].Msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el ParserErrorList) Sort() {
|
||||||
|
sort.Sort(el)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el ParserErrorList) Error() string {
|
||||||
|
switch len(el) {
|
||||||
|
case 0:
|
||||||
|
return "no errors"
|
||||||
|
case 1:
|
||||||
|
return el[0].Error()
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s (and %d more errors)", el[0], len(el)-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el ParserErrorList) Err() error {
|
||||||
|
if len(el) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return el
|
||||||
|
}
|
||||||
|
|
||||||
|
func (el ParserErrorList) Is(other error) bool {
|
||||||
|
if _, ok := other.(ParserErrorList); ok {
|
||||||
|
return el.Error() == other.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := other.(*ParserErrorList); ok {
|
||||||
|
return el.Error() == (*v).Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintParserError(w io.Writer, err error) {
|
||||||
|
if list, ok := err.(ParserErrorList); ok {
|
||||||
|
for _, e := range list {
|
||||||
|
fmt.Fprintf(w, "%s\n", e)
|
||||||
|
}
|
||||||
|
} else if err != nil {
|
||||||
|
fmt.Fprintf(w, "%s\n", err)
|
||||||
|
}
|
||||||
|
}
|
1028
argh/parser_test.go
Normal file
1028
argh/parser_test.go
Normal file
File diff suppressed because it is too large
Load Diff
94
argh/querier.go
Normal file
94
argh/querier.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package argh
|
||||||
|
|
||||||
|
type Querier interface {
|
||||||
|
Program() (*Command, bool)
|
||||||
|
AST() []Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewQuerier(nodes []Node) Querier {
|
||||||
|
return &defaultQuerier{nodes: nodes}
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultQuerier struct {
|
||||||
|
nodes []Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dq *defaultQuerier) Program() (*Command, bool) {
|
||||||
|
if len(dq.nodes) == 0 {
|
||||||
|
tracef("Program nodes are empty")
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
tracef("Program node[0] is %T", dq.nodes[0])
|
||||||
|
|
||||||
|
v, ok := dq.nodes[0].(*Command)
|
||||||
|
if ok && v.Name == "" {
|
||||||
|
return v, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dq *defaultQuerier) AST() []Node {
|
||||||
|
ret := []Node{}
|
||||||
|
|
||||||
|
for i, node := range dq.nodes {
|
||||||
|
tracef("AST i=%d node type=%T", i, node)
|
||||||
|
|
||||||
|
if _, ok := node.(*ArgDelimiter); ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := node.(*StopFlag); ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := node.(*CompoundShortFlag); ok {
|
||||||
|
if v.Nodes != nil {
|
||||||
|
ret = append(ret, NewQuerier(v.Nodes).AST()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := node.(*Command); ok {
|
||||||
|
astNodes := NewQuerier(v.Nodes).AST()
|
||||||
|
|
||||||
|
if len(astNodes) == 0 {
|
||||||
|
astNodes = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(
|
||||||
|
ret,
|
||||||
|
&Command{
|
||||||
|
Name: v.Name,
|
||||||
|
Values: v.Values,
|
||||||
|
Nodes: astNodes,
|
||||||
|
})
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := node.(*Flag); ok {
|
||||||
|
astNodes := NewQuerier(v.Nodes).AST()
|
||||||
|
|
||||||
|
if len(astNodes) == 0 {
|
||||||
|
astNodes = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(
|
||||||
|
ret,
|
||||||
|
&Flag{
|
||||||
|
Name: v.Name,
|
||||||
|
Values: v.Values,
|
||||||
|
Nodes: astNodes,
|
||||||
|
})
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = append(ret, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
60
argh/querier_test.go
Normal file
60
argh/querier_test.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package argh_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.meatballhat.com/x/box-o-sand/argh"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestQuerier_Program(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
name string
|
||||||
|
args []string
|
||||||
|
cfg *argh.ParserConfig
|
||||||
|
exp string
|
||||||
|
expOK bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "typical",
|
||||||
|
args: []string{"pizzas", "ahoy", "--treatsa", "fun"},
|
||||||
|
cfg: &argh.ParserConfig{
|
||||||
|
Prog: argh.CommandConfig{
|
||||||
|
Commands: &argh.Commands{
|
||||||
|
Map: map[string]argh.CommandConfig{
|
||||||
|
"ahoy": argh.CommandConfig{
|
||||||
|
Flags: &argh.Flags{
|
||||||
|
Map: map[string]argh.FlagConfig{
|
||||||
|
"treatsa": argh.FlagConfig{NValue: 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exp: "pizzas",
|
||||||
|
expOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "minimal",
|
||||||
|
args: []string{"pizzas"},
|
||||||
|
exp: "pizzas",
|
||||||
|
expOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid",
|
||||||
|
args: []string{},
|
||||||
|
expOK: false,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.name, func(ct *testing.T) {
|
||||||
|
pt, err := argh.ParseArgs(tc.args, tc.cfg)
|
||||||
|
require.Nil(ct, err)
|
||||||
|
|
||||||
|
prog, ok := argh.NewQuerier(pt.Nodes).Program()
|
||||||
|
require.Equal(ct, tc.expOK, ok)
|
||||||
|
require.Equal(ct, tc.exp, prog.Name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
159
argh/scanner.go
Normal file
159
argh/scanner.go
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
package argh
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Scanner struct {
|
||||||
|
r *bufio.Reader
|
||||||
|
i int
|
||||||
|
cfg *ScannerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewScanner(r io.Reader, cfg *ScannerConfig) *Scanner {
|
||||||
|
if cfg == nil {
|
||||||
|
cfg = POSIXyScannerConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Scanner{
|
||||||
|
r: bufio.NewReader(r),
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) Scan() (Token, string, Pos) {
|
||||||
|
ch, pos := s.read()
|
||||||
|
|
||||||
|
if s.cfg.IsBlankspace(ch) {
|
||||||
|
_ = s.unread()
|
||||||
|
return s.scanBlankspace()
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.cfg.IsAssignmentOperator(ch) {
|
||||||
|
return ASSIGN, string(ch), pos
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.cfg.IsMultiValueDelim(ch) {
|
||||||
|
return MULTI_VALUE_DELIMITER, string(ch), pos
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch == eol {
|
||||||
|
return EOL, "", pos
|
||||||
|
}
|
||||||
|
|
||||||
|
if ch == nul {
|
||||||
|
return ARG_DELIMITER, string(ch), pos
|
||||||
|
}
|
||||||
|
|
||||||
|
if unicode.IsGraphic(ch) {
|
||||||
|
_ = s.unread()
|
||||||
|
return s.scanArg()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ILLEGAL, string(ch), pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) read() (rune, Pos) {
|
||||||
|
ch, _, err := s.r.ReadRune()
|
||||||
|
s.i++
|
||||||
|
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
return eol, Pos(s.i)
|
||||||
|
} else if err != nil {
|
||||||
|
log.Printf("unknown scanner error=%+v", err)
|
||||||
|
return eol, Pos(s.i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ch, Pos(s.i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) unread() Pos {
|
||||||
|
_ = s.r.UnreadRune()
|
||||||
|
s.i--
|
||||||
|
return Pos(s.i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanBlankspace() (Token, string, Pos) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
ch, pos := s.read()
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
|
||||||
|
for {
|
||||||
|
ch, pos = s.read()
|
||||||
|
|
||||||
|
if ch == eol {
|
||||||
|
break
|
||||||
|
} else if !s.cfg.IsBlankspace(ch) {
|
||||||
|
pos = s.unread()
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
_, _ = buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return BS, buf.String(), pos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scanner) scanArg() (Token, string, Pos) {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
ch, pos := s.read()
|
||||||
|
buf.WriteRune(ch)
|
||||||
|
|
||||||
|
for {
|
||||||
|
ch, pos = s.read()
|
||||||
|
|
||||||
|
if ch == eol || ch == nul || s.cfg.IsAssignmentOperator(ch) || s.cfg.IsMultiValueDelim(ch) {
|
||||||
|
pos = s.unread()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = buf.WriteRune(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
str := buf.String()
|
||||||
|
|
||||||
|
if len(str) == 0 {
|
||||||
|
return EMPTY, str, pos
|
||||||
|
}
|
||||||
|
|
||||||
|
ch0 := rune(str[0])
|
||||||
|
|
||||||
|
if len(str) == 1 {
|
||||||
|
if s.cfg.IsFlagPrefix(ch0) {
|
||||||
|
return STDIN_FLAG, str, pos
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.cfg.IsAssignmentOperator(ch0) {
|
||||||
|
return ASSIGN, str, pos
|
||||||
|
}
|
||||||
|
|
||||||
|
return IDENT, str, pos
|
||||||
|
}
|
||||||
|
|
||||||
|
ch1 := rune(str[1])
|
||||||
|
|
||||||
|
if len(str) == 2 {
|
||||||
|
if s.cfg.IsFlagPrefix(ch0) && s.cfg.IsFlagPrefix(ch1) {
|
||||||
|
return STOP_FLAG, str, pos
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.cfg.IsFlagPrefix(ch0) {
|
||||||
|
return SHORT_FLAG, str, pos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.cfg.IsFlagPrefix(ch0) {
|
||||||
|
if s.cfg.IsFlagPrefix(ch1) {
|
||||||
|
return LONG_FLAG, str, pos
|
||||||
|
}
|
||||||
|
|
||||||
|
return COMPOUND_SHORT_FLAG, str, pos
|
||||||
|
}
|
||||||
|
|
||||||
|
return IDENT, str, pos
|
||||||
|
}
|
39
argh/scanner_config.go
Normal file
39
argh/scanner_config.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package argh
|
||||||
|
|
||||||
|
var (
|
||||||
|
// POSIXyScannerConfig defines a scanner config that uses '-'
|
||||||
|
// as the flag prefix, which also means that "--" is the "long
|
||||||
|
// flag" prefix, a bare "--" is considered STOP_FLAG, and a
|
||||||
|
// bare "-" is considered STDIN_FLAG.
|
||||||
|
POSIXyScannerConfig = &ScannerConfig{
|
||||||
|
AssignmentOperator: '=',
|
||||||
|
FlagPrefix: '-',
|
||||||
|
MultiValueDelim: ',',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type ScannerConfig struct {
|
||||||
|
AssignmentOperator rune
|
||||||
|
FlagPrefix rune
|
||||||
|
MultiValueDelim rune
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ScannerConfig) IsFlagPrefix(ch rune) bool {
|
||||||
|
return ch == cfg.FlagPrefix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ScannerConfig) IsMultiValueDelim(ch rune) bool {
|
||||||
|
return ch == cfg.MultiValueDelim
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ScannerConfig) IsAssignmentOperator(ch rune) bool {
|
||||||
|
return ch == cfg.AssignmentOperator
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ScannerConfig) IsBlankspace(ch rune) bool {
|
||||||
|
return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ScannerConfig) IsUnderscore(ch rune) bool {
|
||||||
|
return ch == '_'
|
||||||
|
}
|
53
argh/token.go
Normal file
53
argh/token.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//go:generate stringer -type Token
|
||||||
|
|
||||||
|
package argh
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
ILLEGAL Token = iota
|
||||||
|
EOL
|
||||||
|
EMPTY // ''
|
||||||
|
BS // ' ' '\t' '\n'
|
||||||
|
IDENT // char group without flag prefix: 'some' 'words'
|
||||||
|
ARG_DELIMITER // rune(0)
|
||||||
|
ASSIGN // '='
|
||||||
|
MULTI_VALUE_DELIMITER // ','
|
||||||
|
LONG_FLAG // char group with double flag prefix: '--flag'
|
||||||
|
SHORT_FLAG // single char with single flag prefix: '-f'
|
||||||
|
COMPOUND_SHORT_FLAG // char group with single flag prefix: '-flag'
|
||||||
|
STDIN_FLAG // '-'
|
||||||
|
STOP_FLAG // '--'
|
||||||
|
|
||||||
|
nul = rune(0)
|
||||||
|
eol = rune(-1)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Token int
|
||||||
|
|
||||||
|
// Position is adapted from go/token.Position
|
||||||
|
type Position struct {
|
||||||
|
Column int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Position) IsValid() bool { return p.Column > 0 }
|
||||||
|
|
||||||
|
func (p Position) String() string {
|
||||||
|
s := ""
|
||||||
|
if p.IsValid() {
|
||||||
|
s = fmt.Sprintf("%d", p.Column)
|
||||||
|
}
|
||||||
|
if s == "" {
|
||||||
|
s = "-"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pos is borrowed from go/token.Pos
|
||||||
|
type Pos int
|
||||||
|
|
||||||
|
const NoPos Pos = 0
|
||||||
|
|
||||||
|
func (p Pos) IsValid() bool {
|
||||||
|
return p != NoPos
|
||||||
|
}
|
35
argh/token_string.go
Normal file
35
argh/token_string.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Code generated by "stringer -type Token"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package argh
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[ILLEGAL-0]
|
||||||
|
_ = x[EOL-1]
|
||||||
|
_ = x[EMPTY-2]
|
||||||
|
_ = x[BS-3]
|
||||||
|
_ = x[IDENT-4]
|
||||||
|
_ = x[ARG_DELIMITER-5]
|
||||||
|
_ = x[ASSIGN-6]
|
||||||
|
_ = x[MULTI_VALUE_DELIMITER-7]
|
||||||
|
_ = x[LONG_FLAG-8]
|
||||||
|
_ = x[SHORT_FLAG-9]
|
||||||
|
_ = x[COMPOUND_SHORT_FLAG-10]
|
||||||
|
_ = x[STDIN_FLAG-11]
|
||||||
|
_ = x[STOP_FLAG-12]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Token_name = "ILLEGALEOLEMPTYBSIDENTARG_DELIMITERASSIGNMULTI_VALUE_DELIMITERLONG_FLAGSHORT_FLAGCOMPOUND_SHORT_FLAGSTDIN_FLAGSTOP_FLAG"
|
||||||
|
|
||||||
|
var _Token_index = [...]uint8{0, 7, 10, 15, 17, 22, 35, 41, 62, 71, 81, 100, 110, 119}
|
||||||
|
|
||||||
|
func (i Token) String() string {
|
||||||
|
if i < 0 || i >= Token(len(_Token_index)-1) {
|
||||||
|
return "Token(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _Token_name[_Token_index[i]:_Token_index[i+1]]
|
||||||
|
}
|
116
explode_mbox.py
116
explode_mbox.py
@@ -1,116 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import gzip
|
|
||||||
import hashlib
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import pathlib
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import typing
|
|
||||||
|
|
||||||
|
|
||||||
def main(sysargs=sys.argv[:]) -> int:
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("mbox", type=argparse.FileType("rb"))
|
|
||||||
parser.add_argument("output_directory", type=pathlib.Path)
|
|
||||||
parser.add_argument(
|
|
||||||
"-s",
|
|
||||||
"--zzz",
|
|
||||||
default=0.005,
|
|
||||||
type=float,
|
|
||||||
help="sleep seconds in between messages",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"-D",
|
|
||||||
"--debug",
|
|
||||||
action="store_true",
|
|
||||||
help="increase logging verbosity to debug level",
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args(sysargs[1:])
|
|
||||||
|
|
||||||
log_level = logging.INFO
|
|
||||||
if os.environ.get("DEBUG") == "enabled":
|
|
||||||
log_level = logging.DEBUG
|
|
||||||
|
|
||||||
logging.basicConfig(level=log_level)
|
|
||||||
|
|
||||||
MBoxExploder().explode(
|
|
||||||
mbox=args.mbox, output_directory=args.output_directory, pause_seconds=args.zzz
|
|
||||||
)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
class MBoxMessage:
|
|
||||||
def __init__(self):
|
|
||||||
self.lines = []
|
|
||||||
|
|
||||||
def as_gz_bytes(self) -> bytes:
|
|
||||||
return gzip.compress(self.as_bytes())
|
|
||||||
|
|
||||||
def as_bytes(self) -> bytes:
|
|
||||||
return b"".join([l for l in self.lines])
|
|
||||||
|
|
||||||
def signature(self) -> str:
|
|
||||||
return hashlib.sha512(self.as_bytes()).hexdigest()
|
|
||||||
|
|
||||||
def relpath(self) -> str:
|
|
||||||
sig = self.signature()
|
|
||||||
return os.path.sep.join([sig[0:2], sig[2:4], sig])
|
|
||||||
|
|
||||||
def gz_relpath(self) -> str:
|
|
||||||
return self.relpath() + ".gz"
|
|
||||||
|
|
||||||
|
|
||||||
class MBoxExploder:
|
|
||||||
def __init__(self):
|
|
||||||
self._log = logging.getLogger().getChild("mbox-exploder")
|
|
||||||
|
|
||||||
def explode(
|
|
||||||
self,
|
|
||||||
mbox: typing.BinaryIO,
|
|
||||||
output_directory: pathlib.Path,
|
|
||||||
pause_seconds: float,
|
|
||||||
):
|
|
||||||
for i, msg in enumerate(self._iter_mbox(mbox)):
|
|
||||||
if len(msg.lines) < 2:
|
|
||||||
self._log.warn("skipping invalid message (%r)", i)
|
|
||||||
continue
|
|
||||||
dest = output_directory / msg.gz_relpath()
|
|
||||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
self._log.info("writing message to %s", str(dest))
|
|
||||||
dest.write_bytes(msg.as_gz_bytes())
|
|
||||||
time.sleep(pause_seconds)
|
|
||||||
|
|
||||||
def _iter_mbox(
|
|
||||||
self, mbox: typing.BinaryIO
|
|
||||||
) -> typing.Generator[MBoxMessage, None, None]:
|
|
||||||
msg = MBoxMessage()
|
|
||||||
cur_line = b""
|
|
||||||
|
|
||||||
while True:
|
|
||||||
byte = mbox.read(1)
|
|
||||||
if len(byte) == 0:
|
|
||||||
self._log.debug("reached EOF")
|
|
||||||
msg.lines.append(cur_line)
|
|
||||||
yield msg
|
|
||||||
return
|
|
||||||
|
|
||||||
cur_line += byte
|
|
||||||
|
|
||||||
if byte != b"\n":
|
|
||||||
continue
|
|
||||||
|
|
||||||
if cur_line.startswith(b"From ") and len(msg.lines) > 1:
|
|
||||||
self._log.debug("reached new msg")
|
|
||||||
yield msg
|
|
||||||
msg = MBoxMessage()
|
|
||||||
|
|
||||||
self._log.debug("appending line %r", cur_line)
|
|
||||||
msg.lines.append(cur_line)
|
|
||||||
cur_line = b""
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
178
guessing_game/Cargo.lock
generated
178
guessing_game/Cargo.lock
generated
@@ -1,178 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cloudabi"
|
|
||||||
version = "0.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fuchsia-cprng"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "guessing_game"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"rand",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.80"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand"
|
|
||||||
version = "0.6.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"libc",
|
|
||||||
"rand_chacha",
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
"rand_hc",
|
|
||||||
"rand_isaac",
|
|
||||||
"rand_jitter",
|
|
||||||
"rand_os",
|
|
||||||
"rand_pcg",
|
|
||||||
"rand_xorshift",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_chacha"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_core"
|
|
||||||
version = "0.4.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_hc"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_isaac"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_jitter"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_os"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
|
||||||
dependencies = [
|
|
||||||
"cloudabi",
|
|
||||||
"fuchsia-cprng",
|
|
||||||
"libc",
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
"rdrand",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_pcg"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"rand_core 0.4.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rand_xorshift"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rdrand"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
|
||||||
dependencies = [
|
|
||||||
"rand_core 0.3.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-i686-pc-windows-gnu",
|
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
@@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
|
207
hyrule/README.md
Normal file
207
hyrule/README.md
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
# Hyrule
|
||||||
|
|
||||||
|
A card game for 2-7 players.
|
||||||
|
|
||||||
|
## requirements
|
||||||
|
|
||||||
|
Use a standard card playing deck, keeping Jokers. Shuffle however
|
||||||
|
you like.
|
||||||
|
|
||||||
|
## dealing
|
||||||
|
|
||||||
|
Each player is dealt 7 cards, kept secret from other players.
|
||||||
|
|
||||||
|
## turns
|
||||||
|
|
||||||
|
There are two variations for taking turns. "Chance rules" is
|
||||||
|
strictly a matter of chance, and is recommended for impatient
|
||||||
|
players because it tends to be faster-paced and has more of a
|
||||||
|
*surprise!* element. "Strategy rules" introduces an element of
|
||||||
|
strategy and is better suited for players with more _patience_.
|
||||||
|
|
||||||
|
### chance rules
|
||||||
|
|
||||||
|
Each player selects one card and places it face down in the middle
|
||||||
|
playing area between themselves and other players.
|
||||||
|
|
||||||
|
On the count of 3, all said together in rhythm, each player flips
|
||||||
|
over their own card. Precedence applies and the winning player
|
||||||
|
takes all cards.
|
||||||
|
|
||||||
|
### strategy rules
|
||||||
|
|
||||||
|
On the first turn or when an equal number of cards have been
|
||||||
|
captured by each player, the tallest player goes first. On each
|
||||||
|
subsequent turn, the player with the most captured cards plays
|
||||||
|
first. The play order then proceeds counter-clockwise.
|
||||||
|
|
||||||
|
When taking one's turn, the player may choose to either *play* or
|
||||||
|
*swap*.
|
||||||
|
|
||||||
|
#### strategy *play*
|
||||||
|
|
||||||
|
When choosing to *play*, the player selects a card and places it
|
||||||
|
face-up in the center play area. The remaining players take turns
|
||||||
|
counter-clockwise, each placing a card face-up in the center play
|
||||||
|
area. Precedence rules apply and the winning player takes all
|
||||||
|
cards.
|
||||||
|
|
||||||
|
#### strategy *swap*
|
||||||
|
|
||||||
|
When choosing to *swap*, the player discards one card face-up in
|
||||||
|
the discard pile (next to the stock pile) and then draws a card
|
||||||
|
from the stock, which should be laying face-down. The goal here may
|
||||||
|
be to take a chance at getting a better card than one is
|
||||||
|
discarding, or to force the next player in the rotation to play, or
|
||||||
|
both.
|
||||||
|
|
||||||
|
> **NOTE**: a player may only choose to *swap* if the player on the
|
||||||
|
> previous turn _did not swap_.
|
||||||
|
|
||||||
|
## precedence
|
||||||
|
|
||||||
|
The following rules apply across suits with the exception of
|
||||||
|
*jokers and fives* (explained below). Cards within a single suit
|
||||||
|
are compared with higher-value cards winning. Cards are counted
|
||||||
|
from ace (1) through king (13).
|
||||||
|
|
||||||
|
Ways to think about these rules could include:
|
||||||
|
|
||||||
|
- "rupee buys bomb" / money buys weapon
|
||||||
|
- "rupee buys sword" / money buys weapon
|
||||||
|
- "bomb blows up sword" / range weapon beats melee weapon
|
||||||
|
- "bomb blows up heart" / range weapon beats unarmed
|
||||||
|
- "sword cuts through heart" / melee weapon beats unarmed
|
||||||
|
- "heart is stronger than rupee" / love conquers money
|
||||||
|
|
||||||
|
**NOTE:** In the case of a turn that involves 3 or more cards, the
|
||||||
|
presence of both diamonds *and* hearts will result in the
|
||||||
|
highest-value heart winning the turn.
|
||||||
|
|
||||||
|
### diamonds ("rupees")
|
||||||
|
|
||||||
|
Beats:
|
||||||
|
|
||||||
|
- clubs ("bombs")
|
||||||
|
- spades ("swords")
|
||||||
|
|
||||||
|
### clubs ("bombs")
|
||||||
|
|
||||||
|
Beats:
|
||||||
|
|
||||||
|
- spades ("swords")
|
||||||
|
- hearts
|
||||||
|
|
||||||
|
### spades ("swords")
|
||||||
|
|
||||||
|
Beats:
|
||||||
|
|
||||||
|
- hearts
|
||||||
|
|
||||||
|
### hearts
|
||||||
|
|
||||||
|
Beats:
|
||||||
|
|
||||||
|
- diamonds ("rupees")
|
||||||
|
|
||||||
|
### jokers ("tingles")
|
||||||
|
|
||||||
|
Beats everything except *fives*
|
||||||
|
|
||||||
|
### fives
|
||||||
|
|
||||||
|
The 5 of a given suit will be granted special status *only* when
|
||||||
|
played against a joker ("tingle") and will win with the following
|
||||||
|
sub-precedence, which is roughly the inverse of the main
|
||||||
|
precedence:
|
||||||
|
|
||||||
|
- 5 of hearts
|
||||||
|
- 5 of spades / "master sword"
|
||||||
|
- 5 of clubs / "bomb cluster"
|
||||||
|
- 5 of diamonds / "blue rupee"
|
||||||
|
|
||||||
|
## scoring
|
||||||
|
|
||||||
|
At the conclusion of a round of 7 cards, the player with the most
|
||||||
|
cards is the winner. The face values of the cards are not
|
||||||
|
considered at scoring time. A draw may be handled in a "run-off
|
||||||
|
game" or ignored as you like.
|
||||||
|
|
||||||
|
## examples
|
||||||
|
|
||||||
|
In the following examples, the **winning card** is highlighted at
|
||||||
|
the top of each list, followed by an explanation for the outcome.
|
||||||
|
|
||||||
|
### 2-player
|
||||||
|
|
||||||
|
- **2 of clubs**
|
||||||
|
- 2 of spades
|
||||||
|
|
||||||
|
Clubs are higher-value than spades, or "bomb beats sword".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **2 of clubs**
|
||||||
|
- ace of clubs
|
||||||
|
|
||||||
|
Cards within the same suit are compared at face value with aces
|
||||||
|
being *1*.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **ace of hearts**
|
||||||
|
- king of diamonds
|
||||||
|
|
||||||
|
Hearts take precedence over diamonds, or "love is stronger than
|
||||||
|
money".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **joker**
|
||||||
|
- king of hearts
|
||||||
|
|
||||||
|
The opposing card is not a *five*, or "tingle takes _(thing)_".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **5 of spades**
|
||||||
|
- joker
|
||||||
|
|
||||||
|
The 5 of any suit will beat a joker given its special item status
|
||||||
|
in that scenario, or "tingle is distracted by the beauty of
|
||||||
|
_(thing)_".
|
||||||
|
|
||||||
|
### 3-player
|
||||||
|
|
||||||
|
- **2 of diamonds**
|
||||||
|
- 2 of clubs
|
||||||
|
- 2 of spades
|
||||||
|
|
||||||
|
Diamonds have higher precedence than clubs and spades, and there
|
||||||
|
are no hearts present.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **6 of clubs**
|
||||||
|
- 6 of spades
|
||||||
|
- 6 of hearts
|
||||||
|
|
||||||
|
Clubs have higher precedence than spades and hearts, and there are
|
||||||
|
no diamonds present.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **8 of hearts**
|
||||||
|
- 8 of diamonds
|
||||||
|
- 8 of spades
|
||||||
|
|
||||||
|
When both diamonds and hearts are present, hearts is highest
|
||||||
|
precedence.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- **king of diamonds**
|
||||||
|
- jack of diamonds
|
||||||
|
- queen of diamonds
|
||||||
|
|
||||||
|
Within the same suit, cards are compared by face value.
|
BIN
hyrule/README.pdf
Normal file
BIN
hyrule/README.pdf
Normal file
Binary file not shown.
86
hyrule/cards.py
Normal file
86
hyrule/cards.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import dataclasses
|
||||||
|
import enum
|
||||||
|
import random
|
||||||
|
|
||||||
|
Suit = enum.Enum(
|
||||||
|
"Suit",
|
||||||
|
"""
|
||||||
|
CLUBS
|
||||||
|
DIAMONDS
|
||||||
|
HEARTS
|
||||||
|
SPADES
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
FaceValue = enum.Enum(
|
||||||
|
"FaceValue",
|
||||||
|
"""
|
||||||
|
ACE
|
||||||
|
TWO
|
||||||
|
THREE
|
||||||
|
FOUR
|
||||||
|
FIVE
|
||||||
|
SIX
|
||||||
|
SEVEN
|
||||||
|
EIGHT
|
||||||
|
NINE
|
||||||
|
TEN
|
||||||
|
JACK
|
||||||
|
QUEEN
|
||||||
|
KING
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Card:
|
||||||
|
suit: Suit
|
||||||
|
face_value: FaceValue
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.face_value} of {self.suit}"
|
||||||
|
|
||||||
|
|
||||||
|
class Joker:
|
||||||
|
def __str__(self):
|
||||||
|
return "JOKER"
|
||||||
|
|
||||||
|
|
||||||
|
class Deck:
|
||||||
|
def __init__(self, n_jokers=2):
|
||||||
|
self._cards = list(Deck.generate(n_jokers=n_jokers))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Deck len={len(self)}>"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
as_string = []
|
||||||
|
for card in self._cards:
|
||||||
|
as_string.append(str(card))
|
||||||
|
return "\n".join(as_string)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._cards)
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
if len(self._cards) > 0:
|
||||||
|
return self._cards.pop()
|
||||||
|
return None
|
||||||
|
|
||||||
|
def shuffle(self):
|
||||||
|
random.shuffle(self._cards)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def cut(self):
|
||||||
|
cut_point = random.randint(0, len(self))
|
||||||
|
self._cards = self._cards[cut_point:] + self._cards[:cut_point]
|
||||||
|
return self
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def generate(n_jokers=2):
|
||||||
|
for _ in range(n_jokers):
|
||||||
|
yield Joker()
|
||||||
|
|
||||||
|
for suit in Suit:
|
||||||
|
for face_value in FaceValue:
|
||||||
|
yield Card(suit=suit, face_value=face_value)
|
66
hyrule/hyrule.py
Normal file
66
hyrule/hyrule.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import dataclasses
|
||||||
|
import pprint
|
||||||
|
import sys
|
||||||
|
import typing
|
||||||
|
|
||||||
|
import cards
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--player-count", "-c", default=2, type=int, help="The number of players"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--turn-count",
|
||||||
|
"-n",
|
||||||
|
default=5,
|
||||||
|
type=int,
|
||||||
|
help="The number of turns to play",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
Hyrule(player_count=args.player_count, turn_count=args.turn_count).play()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class Player:
|
||||||
|
index: int
|
||||||
|
hand: typing.Set[cards.Card]
|
||||||
|
captured: typing.Set[cards.Card]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def score(self):
|
||||||
|
return len(self.captured)
|
||||||
|
|
||||||
|
|
||||||
|
class Hyrule:
|
||||||
|
def __init__(self, player_count=2, turn_count=5):
|
||||||
|
self._players = [
|
||||||
|
Player(index=i, hand=set(), captured=set()) for i in range(player_count)
|
||||||
|
]
|
||||||
|
self._turn_count = turn_count
|
||||||
|
|
||||||
|
def play(self):
|
||||||
|
for turn in self._each_turn():
|
||||||
|
self._show_turn(turn)
|
||||||
|
|
||||||
|
def _each_turn(self):
|
||||||
|
for turn_number in range(self._turn_count):
|
||||||
|
yield self._simulate_turn(turn_number)
|
||||||
|
|
||||||
|
def _simulate_turn(self, turn_number):
|
||||||
|
...
|
||||||
|
|
||||||
|
def _show_turn(self, turn):
|
||||||
|
print(turn)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
2
hyrule/justfile
Normal file
2
hyrule/justfile
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
build:
|
||||||
|
pandoc -r markdown -w pdf -o README.pdf README.md
|
85
rustbook/guessing_game/Cargo.lock
generated
Normal file
85
rustbook/guessing_game/Cargo.lock
generated
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "guessing_game"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.80"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
"rand_hc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
@@ -7,4 +7,4 @@ edition = "2018"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.6.0"
|
rand = "0.8.3"
|
@@ -5,7 +5,7 @@ use std::io;
|
|||||||
fn main() {
|
fn main() {
|
||||||
println!("Guess the number!");
|
println!("Guess the number!");
|
||||||
|
|
||||||
let secret_number = rand::thread_rng().gen_range(1, 101);
|
let secret_number = rand::thread_rng().gen_range(1..=100);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
println!("Please input your guess.");
|
println!("Please input your guess.");
|
27
rustbook/hello_cargo/src/main.rs
Normal file
27
rustbook/hello_cargo/src/main.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
const SEP: &str = "\n---- bleh ----\n";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
|
||||||
|
println!("{}", SEP);
|
||||||
|
|
||||||
|
println!("0x{:x}", 0b1111_1010_1111_1010_1111_1010_1111);
|
||||||
|
println!("0b{:b}", 0xfafafaf);
|
||||||
|
println!("{}", 0xfafafaf);
|
||||||
|
println!("{}", 0xfeefee);
|
||||||
|
println!("0b{:b}", 0xfeefee);
|
||||||
|
|
||||||
|
println!("{}", SEP);
|
||||||
|
|
||||||
|
let sum = 5 + 10;
|
||||||
|
let difference = 95.5 - 4.3;
|
||||||
|
let product = 4 * 30;
|
||||||
|
let quotient = 56.7 / 32.2;
|
||||||
|
let remainder = 43 % 5;
|
||||||
|
|
||||||
|
println!("sum: {}", sum);
|
||||||
|
println!("difference: {}", difference);
|
||||||
|
println!("product: {}", product);
|
||||||
|
println!("quotient: {}", quotient);
|
||||||
|
println!("remainder: {}", remainder);
|
||||||
|
}
|
BIN
rustbook/hello_world/main
Executable file
BIN
rustbook/hello_world/main
Executable file
Binary file not shown.
7
rustbyexample/.gitignore
vendored
7
rustbyexample/.gitignore
vendored
@@ -1,4 +1,11 @@
|
|||||||
|
/custom_types/*
|
||||||
/hello/*
|
/hello/*
|
||||||
/primitives/*
|
/primitives/*
|
||||||
|
/variable_bindings/*
|
||||||
|
/types/*
|
||||||
|
|
||||||
|
!/custom_types/*.rs
|
||||||
!/hello/*.rs
|
!/hello/*.rs
|
||||||
!/primitives/*.rs
|
!/primitives/*.rs
|
||||||
|
!/variable_bindings/*.rs
|
||||||
|
!/types/*.rs
|
||||||
|
16
rustbyexample/custom_types/custom_types_constants.rs
Normal file
16
rustbyexample/custom_types/custom_types_constants.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
static LANGUAGE: &str = "Rust";
|
||||||
|
const THRESHOLD: i32 = 10;
|
||||||
|
|
||||||
|
fn is_big(n: i32) -> bool {
|
||||||
|
n > THRESHOLD
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let n = 16;
|
||||||
|
|
||||||
|
println!("This is {}", LANGUAGE);
|
||||||
|
println!("The threshold is {}", THRESHOLD);
|
||||||
|
println!("{} is {}", n, if is_big(n) { "big" } else { "small" });
|
||||||
|
|
||||||
|
// THRESHOLD = 5;
|
||||||
|
}
|
33
rustbyexample/custom_types/custom_types_enum.rs
Normal file
33
rustbyexample/custom_types/custom_types_enum.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
enum WebEvent {
|
||||||
|
PageLoad,
|
||||||
|
PageUnload,
|
||||||
|
KeyPress(char),
|
||||||
|
Paste(String),
|
||||||
|
Click { x: i64, y: i64 },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inspect(event: WebEvent) {
|
||||||
|
match event {
|
||||||
|
WebEvent::PageLoad => println!("page loaded"),
|
||||||
|
WebEvent::PageUnload => println!("page unloaded"),
|
||||||
|
WebEvent::KeyPress(c) => println!("pressed '{}'.", c),
|
||||||
|
WebEvent::Paste(s) => println!("pasted \"{}\".", s),
|
||||||
|
WebEvent::Click { x, y } => {
|
||||||
|
println!("clicked at x={}, y={}.", x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let pressed = WebEvent::KeyPress('x');
|
||||||
|
let pasted = WebEvent::Paste("my text".to_owned());
|
||||||
|
let click = WebEvent::Click { x: 20, y: 80 };
|
||||||
|
let load = WebEvent::PageLoad;
|
||||||
|
let unload = WebEvent::PageUnload;
|
||||||
|
|
||||||
|
inspect(pressed);
|
||||||
|
inspect(pasted);
|
||||||
|
inspect(click);
|
||||||
|
inspect(load);
|
||||||
|
inspect(unload);
|
||||||
|
}
|
21
rustbyexample/custom_types/custom_types_enum_c_like.rs
Normal file
21
rustbyexample/custom_types/custom_types_enum_c_like.rs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
enum Number {
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
Two,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Color {
|
||||||
|
Red = 0xff0000,
|
||||||
|
Green = 0x00ff00,
|
||||||
|
Blue = 0x0000ff,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("zero is {}", Number::Zero as i32);
|
||||||
|
println!("one is {}", Number::One as i32);
|
||||||
|
|
||||||
|
println!("roses are #{:06x}", Color::Red as i32);
|
||||||
|
println!("violets are #{:06x}", Color::Blue as i32);
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
use crate::List::*;
|
||||||
|
|
||||||
|
enum List {
|
||||||
|
Cons(u32, Box<List>),
|
||||||
|
Nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl List {
|
||||||
|
fn new() -> List {
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepend(self, elem: u32) -> List {
|
||||||
|
Cons(elem, Box::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> u32 {
|
||||||
|
match *self {
|
||||||
|
Cons(_, ref tail) => 1 + tail.len(),
|
||||||
|
Nil => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stringify(&self) -> String {
|
||||||
|
match *self {
|
||||||
|
Cons(head, ref tail) => {
|
||||||
|
format!("{}, {}", head, tail.stringify())
|
||||||
|
}
|
||||||
|
Nil => {
|
||||||
|
format!("Nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut list = List::new();
|
||||||
|
|
||||||
|
list = list.prepend(1);
|
||||||
|
list = list.prepend(2);
|
||||||
|
list = list.prepend(3);
|
||||||
|
|
||||||
|
println!("linked list has length: {}", list.len());
|
||||||
|
println!("{}", list.stringify());
|
||||||
|
}
|
29
rustbyexample/custom_types/custom_types_enum_use.rs
Normal file
29
rustbyexample/custom_types/custom_types_enum_use.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
Rich,
|
||||||
|
Poor,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Work {
|
||||||
|
Civilian,
|
||||||
|
Soldier,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
use crate::Status::{Poor, Rich};
|
||||||
|
use crate::Work::*;
|
||||||
|
|
||||||
|
let status = Poor;
|
||||||
|
let work = Civilian;
|
||||||
|
|
||||||
|
match status {
|
||||||
|
Rich => println!("The rich have lots of money!"),
|
||||||
|
Poor => println!("The poor have no money..."),
|
||||||
|
}
|
||||||
|
|
||||||
|
match work {
|
||||||
|
Civilian => println!("Civilians work!"),
|
||||||
|
Soldier => println!("Soldiers fight!"),
|
||||||
|
}
|
||||||
|
}
|
79
rustbyexample/custom_types/custom_types_structs.rs
Normal file
79
rustbyexample/custom_types/custom_types_structs.rs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
|
struct Person {
|
||||||
|
name: String,
|
||||||
|
age: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Unit;
|
||||||
|
|
||||||
|
struct Pair(i32, f32);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Point {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Rectangle {
|
||||||
|
top_left: Point,
|
||||||
|
bottom_right: Point,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rect_area(r: Rectangle) -> f32 {
|
||||||
|
(r.top_left.x - r.bottom_right.x) * (r.top_left.y - r.bottom_right.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn square(p: &Point, d: f32) -> Rectangle {
|
||||||
|
Rectangle {
|
||||||
|
top_left: Point { x: p.x + d, y: p.y },
|
||||||
|
bottom_right: Point { x: p.x, y: p.y + d },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let name = String::from("Peter");
|
||||||
|
let age = 27;
|
||||||
|
let peter = Person { name, age };
|
||||||
|
|
||||||
|
println!("{:?}", peter);
|
||||||
|
|
||||||
|
let point: Point = Point { x: 10.3, y: 0.4 };
|
||||||
|
|
||||||
|
println!("point coordinates: ({}, {})", point.x, point.y);
|
||||||
|
|
||||||
|
let bottom_right = Point { x: 5.2, y: 0.2 };
|
||||||
|
|
||||||
|
println!("second point: ({}, {})", bottom_right.x, bottom_right.y);
|
||||||
|
|
||||||
|
let Point {
|
||||||
|
x: left_edge,
|
||||||
|
y: top_edge,
|
||||||
|
} = point;
|
||||||
|
|
||||||
|
let rect = Rectangle {
|
||||||
|
top_left: Point {
|
||||||
|
x: left_edge,
|
||||||
|
y: top_edge,
|
||||||
|
},
|
||||||
|
bottom_right: bottom_right,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _unit = Unit;
|
||||||
|
|
||||||
|
let pair = Pair(1, 0.1);
|
||||||
|
|
||||||
|
println!("pair contains {:?} and {:?}", pair.0, pair.1);
|
||||||
|
|
||||||
|
let Pair(integer, decimal) = pair;
|
||||||
|
|
||||||
|
println!("pair contains {:?} and {:?}", integer, decimal);
|
||||||
|
|
||||||
|
println!("rect in {:?}", rect);
|
||||||
|
println!("area of rect is {}", rect_area(rect));
|
||||||
|
|
||||||
|
let lower_left = Point { x: 4.1, y: 11.3 };
|
||||||
|
let d = 7.7f32;
|
||||||
|
let sq = square(&lower_left, d);
|
||||||
|
println!("sq from point={:?} of d={} is {:?}", lower_left, d, sq);
|
||||||
|
}
|
26
rustbyexample/primitives/primitives_array.rs
Normal file
26
rustbyexample/primitives/primitives_array.rs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
use std::mem;
|
||||||
|
|
||||||
|
fn analyze_slice(slice: &[i32]) {
|
||||||
|
println!("first element of the slice: {}", slice[0]);
|
||||||
|
println!("the slice has {} elements", slice.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let xs: [i32; 5] = [1, 2, 3, 4, 5];
|
||||||
|
let ys: [i32; 500] = [0; 500];
|
||||||
|
|
||||||
|
println!("first element of the array: {}", xs[0]);
|
||||||
|
println!("second element of the array: {}", xs[1]);
|
||||||
|
|
||||||
|
println!("number of elements in the array: {}", xs.len());
|
||||||
|
|
||||||
|
println!("array occupies {} bytes", mem::size_of_val(&xs));
|
||||||
|
|
||||||
|
println!("borrow the whole array as a slice");
|
||||||
|
analyze_slice(&xs);
|
||||||
|
|
||||||
|
println!("borrow a section of the array as a slice");
|
||||||
|
analyze_slice(&ys[1..4]);
|
||||||
|
|
||||||
|
// println!("{}", xs[5]);
|
||||||
|
}
|
15
rustbyexample/types/alias.rs
Normal file
15
rustbyexample/types/alias.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
type NanoSecond = u64;
|
||||||
|
type Inch = u64;
|
||||||
|
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
type u64_t = u64;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let nanoseconds: NanoSecond = 5 as u64_t;
|
||||||
|
let inches: Inch = 2 as u64_t;
|
||||||
|
|
||||||
|
println!("{} nanoseconds + {} inches = {} unit?",
|
||||||
|
nanoseconds,
|
||||||
|
inches,
|
||||||
|
nanoseconds + inches);
|
||||||
|
}
|
52
rustbyexample/types/cast.rs
Normal file
52
rustbyexample/types/cast.rs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#![allow(overflowing_literals)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let decimal = 65.4321_f32;
|
||||||
|
// let integer: u8 = decimal;
|
||||||
|
|
||||||
|
let integer = decimal as u8;
|
||||||
|
let character = integer as char;
|
||||||
|
|
||||||
|
// let character = decimal as char;
|
||||||
|
|
||||||
|
println!("Casting: {} -> {} -> {}", decimal, integer, character);
|
||||||
|
|
||||||
|
println!("1000 as u16 is: {}", 1000 as u16);
|
||||||
|
|
||||||
|
println!("1000 as u8 is: {}", 1000 as u8);
|
||||||
|
|
||||||
|
println!(" -1 as u8 is: {}", (-1i8) as u8);
|
||||||
|
|
||||||
|
println!("1000 mod 256 is: {}", 1000 % 256);
|
||||||
|
|
||||||
|
println!(" 128 as a i16 is: {}", 128 as i16);
|
||||||
|
|
||||||
|
println!(" 128 as a i8 is: {}", 128 as i8);
|
||||||
|
|
||||||
|
println!("1000 as a u8 is: {}", 1000 as u8);
|
||||||
|
|
||||||
|
println!(" 232 as a i8 is: {}", 232 as i8);
|
||||||
|
|
||||||
|
println!("300.0 as u8 is {}", 300.0_f32 as u8);
|
||||||
|
|
||||||
|
println!("-100.0 as u8 is {}", -100.0_f32 as u8);
|
||||||
|
|
||||||
|
println!("nan as u8 is {}", f32::NAN as u8);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
println!(
|
||||||
|
"300.0 as u8 is {} (unchecked)",
|
||||||
|
300.0_f32.to_int_unchecked::<u8>()
|
||||||
|
);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"-100.0 as u8 is {} (unchecked)",
|
||||||
|
(-100.0_f32).to_int_unchecked::<u8>()
|
||||||
|
);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"nan as u8 is {} (unchecked)",
|
||||||
|
f32::NAN.to_int_unchecked::<u8>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
9
rustbyexample/types/inference.rs
Normal file
9
rustbyexample/types/inference.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fn main() {
|
||||||
|
let elem = 5u8;
|
||||||
|
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
|
vec.push(elem);
|
||||||
|
|
||||||
|
println!("{:?}", vec);
|
||||||
|
}
|
14
rustbyexample/types/literals.rs
Normal file
14
rustbyexample/types/literals.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
fn main() {
|
||||||
|
let x = 1u8;
|
||||||
|
let y = 2u32;
|
||||||
|
let z = 3f32;
|
||||||
|
|
||||||
|
let i = 1;
|
||||||
|
let f = 1.0;
|
||||||
|
|
||||||
|
println!("size of `x` in bytes: {}", std::mem::size_of_val(&x));
|
||||||
|
println!("size of `y` in bytes: {}", std::mem::size_of_val(&y));
|
||||||
|
println!("size of `z` in bytes: {}", std::mem::size_of_val(&z));
|
||||||
|
println!("size of `i` in bytes: {}", std::mem::size_of_val(&i));
|
||||||
|
println!("size of `f` in bytes: {}", std::mem::size_of_val(&f));
|
||||||
|
}
|
19
rustbyexample/variable_bindings/declare.rs
Normal file
19
rustbyexample/variable_bindings/declare.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
fn main() {
|
||||||
|
let a_binding;
|
||||||
|
|
||||||
|
{
|
||||||
|
let x = 2;
|
||||||
|
|
||||||
|
a_binding = x * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("a binding: {}", a_binding);
|
||||||
|
|
||||||
|
let another_binding;
|
||||||
|
|
||||||
|
//println!("another binding: {}", another_binding);
|
||||||
|
|
||||||
|
another_binding = 1;
|
||||||
|
|
||||||
|
println!("another binding: {}", another_binding);
|
||||||
|
}
|
11
rustbyexample/variable_bindings/freeze.rs
Normal file
11
rustbyexample/variable_bindings/freeze.rs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
fn main() {
|
||||||
|
let mut _mutable_integer = 7i32;
|
||||||
|
|
||||||
|
{
|
||||||
|
let _mutable_integer = _mutable_integer;
|
||||||
|
|
||||||
|
// _mutable_integer = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mutable_integer = 3;
|
||||||
|
}
|
12
rustbyexample/variable_bindings/mut.rs
Normal file
12
rustbyexample/variable_bindings/mut.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
fn main() {
|
||||||
|
let _immutable_binding = 1;
|
||||||
|
let mut mutable_binding = 1;
|
||||||
|
|
||||||
|
println!("Before mutation: {}", mutable_binding);
|
||||||
|
|
||||||
|
mutable_binding += 1;
|
||||||
|
|
||||||
|
println!("After mutation: {}", mutable_binding);
|
||||||
|
|
||||||
|
// _immutable_binding += 1;
|
||||||
|
}
|
12
rustbyexample/variable_bindings/scope.rs
Normal file
12
rustbyexample/variable_bindings/scope.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
fn main() {
|
||||||
|
let long_lived_binding = 1;
|
||||||
|
|
||||||
|
{
|
||||||
|
let short_lived_binding = 2;
|
||||||
|
println!("inner short: {}", short_lived_binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// println!("outer short: {}", short_lived_binding);
|
||||||
|
|
||||||
|
println!("outer long: {}", long_lived_binding);
|
||||||
|
}
|
16
rustbyexample/variable_bindings/scope2.rs
Normal file
16
rustbyexample/variable_bindings/scope2.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
fn main() {
|
||||||
|
let shadowed_binding = 1;
|
||||||
|
|
||||||
|
{
|
||||||
|
println!("before being shadowed: {}", shadowed_binding);
|
||||||
|
|
||||||
|
let shadowed_binding = "abc";
|
||||||
|
|
||||||
|
println!("shadowed in inner block: {}", shadowed_binding);
|
||||||
|
}
|
||||||
|
println!("outside inner block: {}", shadowed_binding);
|
||||||
|
|
||||||
|
let shadowed_binding = 2;
|
||||||
|
println!("shadowed in outer block: {}", shadowed_binding);
|
||||||
|
}
|
||||||
|
|
15
rustbyexample/variable_bindings/variable_bindings.rs
Normal file
15
rustbyexample/variable_bindings/variable_bindings.rs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
fn main() {
|
||||||
|
let an_integer = 1u32;
|
||||||
|
let a_boolean = true;
|
||||||
|
let unit = ();
|
||||||
|
|
||||||
|
let copied_integer = an_integer;
|
||||||
|
|
||||||
|
println!("An integer: {:?}", copied_integer);
|
||||||
|
println!("A boolean: {:?}", a_boolean);
|
||||||
|
println!("Meet the unit value: {:?}", unit);
|
||||||
|
|
||||||
|
let _unused_variable = 3u32;
|
||||||
|
|
||||||
|
let noisy_unused_variable = 2u32;
|
||||||
|
}
|
@@ -1,54 +0,0 @@
|
|||||||
#!/usr/bin/env oh-no
|
|
||||||
|
|
||||||
set shell := ["python", "-c"]
|
|
||||||
set something
|
|
||||||
|
|
||||||
alias d := dogs
|
|
||||||
|
|
||||||
export HOTDOGPARTY := hats
|
|
||||||
|
|
||||||
now_ish := `print("oh gee")` + `print(sum([1, 2, 5]))`
|
|
||||||
|
|
||||||
an_string := "sit ready \"for reals\" 'uh huh' {{an_raw_string + '{{what' }}"
|
|
||||||
|
|
||||||
an_raw_string := 'sit ready "wow" {{not_gonna}}'
|
|
||||||
|
|
||||||
hats := '''
|
|
||||||
very serious hat's
|
|
||||||
'''
|
|
||||||
|
|
||||||
wats := """
|
|
||||||
{{an_string}} \"yay\" 'ok'
|
|
||||||
|
|
||||||
{{ 'dog' + just_executable()}}
|
|
||||||
|
|
||||||
{{justfile()}}
|
|
||||||
|
|
||||||
{{justfile_directory()}}
|
|
||||||
|
|
||||||
{{arch()}} {{os()}}
|
|
||||||
"""
|
|
||||||
|
|
||||||
serious_business := ```
|
|
||||||
from __future__ import print_function
|
|
||||||
import os
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
print(f"home: {os.environ.get("HOME", "?")}")
|
|
||||||
print(f"now-ish: {datetime.datetime.now()}")
|
|
||||||
```
|
|
||||||
|
|
||||||
dogs: aminal manimal _secret
|
|
||||||
|
|
||||||
# show the aminal by kind
|
|
||||||
aminal kind='dog':
|
|
||||||
#!/usr/bin/env python
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
print('\n'.join(os.environ.keys()), file=sys.stderr)
|
|
||||||
|
|
||||||
_secret +WHATEVER='OK':
|
|
||||||
@print("don't tell anyone {{WHATEVER}}")
|
|
||||||
|
|
||||||
manimal:
|
|
||||||
@print('wow')
|
|
@@ -1 +0,0 @@
|
|||||||
autocmd BufReadPost justfile,*.justfile setfiletype just
|
|
@@ -1,80 +0,0 @@
|
|||||||
" Vim syntax file
|
|
||||||
" Language: Just Command Runner
|
|
||||||
" Maintainer: Dan Buch <dan@meatballhat.com>
|
|
||||||
" Latest Revision: 28 May 2021
|
|
||||||
|
|
||||||
if exists("b:current_syntax")
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
|
|
||||||
let b:shell_syntax = "sh"
|
|
||||||
if exists("g:just_shell_syntax")
|
|
||||||
let b:shell_syntax = g:just_shell_syntax
|
|
||||||
endif
|
|
||||||
|
|
||||||
exe "syn include @setshellsyntax syntax/" . b:shell_syntax . ".vim"
|
|
||||||
|
|
||||||
syn keyword justKeyword alias export if else set
|
|
||||||
|
|
||||||
syn keyword justFunction arch os os_family
|
|
||||||
syn keyword justFunction env_var env_var_or_default invocation_directory
|
|
||||||
syn keyword justFunction justfile justfile_directory just_executable
|
|
||||||
|
|
||||||
syn keyword justSetting shell export dotenv-load positional-arguments
|
|
||||||
|
|
||||||
syn match justOperator "\v:"
|
|
||||||
syn match justOperator "\v\)"
|
|
||||||
syn match justOperator "\v\("
|
|
||||||
syn match justOperator "\v\+"
|
|
||||||
syn match justOperator "\v\@"
|
|
||||||
syn match justOperator "\v\="
|
|
||||||
syn match justOperator "\v:\="
|
|
||||||
|
|
||||||
syn match justQuote '\v"'
|
|
||||||
syn match justQuote "\v'"
|
|
||||||
syn match justQuote '\v"""'
|
|
||||||
syn match justQuote "\v'''"
|
|
||||||
|
|
||||||
syn match justBacktick "\v\`"
|
|
||||||
syn match justBacktick "\v\`\`\`"
|
|
||||||
|
|
||||||
syn match justCurlyBrace "\v\{\{"
|
|
||||||
syn match justCurlyBrace "\v\}\}"
|
|
||||||
|
|
||||||
syn match justComment "\v#.*$"
|
|
||||||
|
|
||||||
syn match justShebang "\v^ *#!.*$"
|
|
||||||
|
|
||||||
syn match justPublicName "\v[a-zA-Z][_a-zA-Z0-9-]*[a-zA-Z0-9_]"
|
|
||||||
syn match justPrivateName "\v_[a-zA-Z0-9][_a-zA-Z0-9-]*[a-zA-Z0-9_]"
|
|
||||||
|
|
||||||
syn region justCurlyBraced matchgroup=justCurlyBrace start="\v\{\{" skip="\v\\\{" end="\v\}\}" contains=justKeyword,justFunction,justOperator,justQuote,justString,justRawString
|
|
||||||
|
|
||||||
syn region justString matchgroup=justQuote start='\v"' skip='\v\\"' end='\v"' contains=justCurlyBraced
|
|
||||||
syn region justString matchgroup=justQuote start='\v"""' skip='\v\\"' end='\v"""' contains=justCurlyBraced
|
|
||||||
|
|
||||||
syn region justRawString matchgroup=justQuote start="\v'" end="\v'"
|
|
||||||
syn region justRawString matchgroup=justQuote start="\v'''" end="\v'''"
|
|
||||||
|
|
||||||
syn region justSubshell matchgroup=justBacktick start="\v\`" skip="\v\\\`" end="\v\`" contains=@setshellsyntax
|
|
||||||
syn region justSubshell matchgroup=justBacktick start="\v\`\`\`" skip="\v\\\`" end="\v\`\`\`" contains=@setshellsyntax
|
|
||||||
syn region justRecipeBody start="\v^ *" end="$" keepend contains=@setshellsyntax
|
|
||||||
syn region justRecipeNoechoBody start="\v^ *\@" end="$" keepend contains=justOperator,@setshellsyntax
|
|
||||||
|
|
||||||
hi def link justPublicName Constant
|
|
||||||
hi def link justPrivateName Constant
|
|
||||||
hi def link justKeyword Keyword
|
|
||||||
hi def link justSetting Keyword
|
|
||||||
hi def link justOperator Operator
|
|
||||||
hi def link justComment Comment
|
|
||||||
hi def link justShebang PreProc
|
|
||||||
hi def link justFunction Function
|
|
||||||
hi def link justQuote String
|
|
||||||
hi def link justString String
|
|
||||||
hi def link justRawString String
|
|
||||||
hi def link justCurlyBrace Keyword
|
|
||||||
hi def link justBacktick Special
|
|
||||||
|
|
||||||
let b:current_syntax = "just"
|
|
||||||
|
|
||||||
" vim:expandtab:ts=2:sts=2
|
|
Reference in New Issue
Block a user