gglang 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gglang-0.2.0/LICENSE +21 -0
- gglang-0.2.0/PKG-INFO +69 -0
- gglang-0.2.0/README.md +54 -0
- gglang-0.2.0/gglang/__init__.py +1 -0
- gglang-0.2.0/gglang/__main__.py +21 -0
- gglang-0.2.0/gglang/runtime.py +175 -0
- gglang-0.2.0/gglang.egg-info/PKG-INFO +69 -0
- gglang-0.2.0/gglang.egg-info/SOURCES.txt +12 -0
- gglang-0.2.0/gglang.egg-info/dependency_links.txt +1 -0
- gglang-0.2.0/gglang.egg-info/entry_points.txt +2 -0
- gglang-0.2.0/gglang.egg-info/top_level.txt +1 -0
- gglang-0.2.0/pyproject.toml +27 -0
- gglang-0.2.0/setup.cfg +4 -0
- gglang-0.2.0/tests/test_golden.py +49 -0
gglang-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 gglang authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
gglang-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gglang
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: 줴줴이랭: "줴줴이야~" 밈으로 만든 난해한 프로그래밍 언어 (엄랭 오마주)
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://gglang.jihwan.kim
|
|
7
|
+
Keywords: esolang,esoteric,korean,interpreter
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Topic :: Software Development :: Interpreters
|
|
10
|
+
Classifier: Natural Language :: Korean
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# 줴줴이랭 (gglang)
|
|
17
|
+
|
|
18
|
+
"줴줴이야~" 밈으로 만든 난해한 프로그래밍 언어. [엄랭](https://github.com/rycont/umjunsik-lang) 오마주.
|
|
19
|
+
|
|
20
|
+
- 플레이그라운드: https://gglang.jihwan.kim
|
|
21
|
+
- 확장자: `.ggya`
|
|
22
|
+
|
|
23
|
+
## 설치
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install gglang
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 실행
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
gglang hello.ggya
|
|
33
|
+
# 또는
|
|
34
|
+
python -m gglang hello.ggya
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 문법 요약
|
|
38
|
+
|
|
39
|
+
| 코드 | 뜻 |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `아뇨` | 1. `아뇨아뇨아뇨`는 3 |
|
|
42
|
+
| 붙여 쓰기 | 더하기. `아뇨아뇨쥐~랄` → 2 + 변수 |
|
|
43
|
+
| 띄어 쓰기 | 곱하기. `아뇨아뇨 아뇨아뇨아뇨` → 2 × 3 |
|
|
44
|
+
| `엄` | 바로 뒤 원소의 부호 뒤집기. `엄아뇨` → -1 |
|
|
45
|
+
| `쥐~랄`, `쥐~~랄`, … | 변수 (물결 수가 변수 번호) |
|
|
46
|
+
| `쥐~랄 식` | 대입 |
|
|
47
|
+
| `아 예?` | 정수 입력 |
|
|
48
|
+
| `티~원 식` | 숫자 출력 |
|
|
49
|
+
| `티~원` | 공백 출력 |
|
|
50
|
+
| `티~~원 식` | 유니코드 문자 하나 출력 |
|
|
51
|
+
| `티~~원` | 줄바꿈 |
|
|
52
|
+
| `요이~` … `요이~` | 반복 (물결 수가 같은 것끼리 짝) |
|
|
53
|
+
| `줴줴이야 식` | 식이 0이면 반복 탈출 |
|
|
54
|
+
|
|
55
|
+
## 예제
|
|
56
|
+
|
|
57
|
+
`GG` 출력:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
쥐~랄 아뇨아뇨아뇨아뇨아뇨아뇨아뇨아뇨 아뇨아뇨아뇨아뇨아뇨아뇨아뇨아뇨아뇨
|
|
61
|
+
쥐~랄 쥐~랄엄아뇨
|
|
62
|
+
티~~원 쥐~랄
|
|
63
|
+
티~~원 쥐~랄
|
|
64
|
+
티~~원
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 라이선스
|
|
68
|
+
|
|
69
|
+
MIT
|
gglang-0.2.0/README.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# 줴줴이랭 (gglang)
|
|
2
|
+
|
|
3
|
+
"줴줴이야~" 밈으로 만든 난해한 프로그래밍 언어. [엄랭](https://github.com/rycont/umjunsik-lang) 오마주.
|
|
4
|
+
|
|
5
|
+
- 플레이그라운드: https://gglang.jihwan.kim
|
|
6
|
+
- 확장자: `.ggya`
|
|
7
|
+
|
|
8
|
+
## 설치
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install gglang
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 실행
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
gglang hello.ggya
|
|
18
|
+
# 또는
|
|
19
|
+
python -m gglang hello.ggya
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 문법 요약
|
|
23
|
+
|
|
24
|
+
| 코드 | 뜻 |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `아뇨` | 1. `아뇨아뇨아뇨`는 3 |
|
|
27
|
+
| 붙여 쓰기 | 더하기. `아뇨아뇨쥐~랄` → 2 + 변수 |
|
|
28
|
+
| 띄어 쓰기 | 곱하기. `아뇨아뇨 아뇨아뇨아뇨` → 2 × 3 |
|
|
29
|
+
| `엄` | 바로 뒤 원소의 부호 뒤집기. `엄아뇨` → -1 |
|
|
30
|
+
| `쥐~랄`, `쥐~~랄`, … | 변수 (물결 수가 변수 번호) |
|
|
31
|
+
| `쥐~랄 식` | 대입 |
|
|
32
|
+
| `아 예?` | 정수 입력 |
|
|
33
|
+
| `티~원 식` | 숫자 출력 |
|
|
34
|
+
| `티~원` | 공백 출력 |
|
|
35
|
+
| `티~~원 식` | 유니코드 문자 하나 출력 |
|
|
36
|
+
| `티~~원` | 줄바꿈 |
|
|
37
|
+
| `요이~` … `요이~` | 반복 (물결 수가 같은 것끼리 짝) |
|
|
38
|
+
| `줴줴이야 식` | 식이 0이면 반복 탈출 |
|
|
39
|
+
|
|
40
|
+
## 예제
|
|
41
|
+
|
|
42
|
+
`GG` 출력:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
쥐~랄 아뇨아뇨아뇨아뇨아뇨아뇨아뇨아뇨 아뇨아뇨아뇨아뇨아뇨아뇨아뇨아뇨아뇨
|
|
46
|
+
쥐~랄 쥐~랄엄아뇨
|
|
47
|
+
티~~원 쥐~랄
|
|
48
|
+
티~~원 쥐~랄
|
|
49
|
+
티~~원
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 라이선스
|
|
53
|
+
|
|
54
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""줴줴이랭 인터프리터."""
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from gglang.runtime import GGError, GGya
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
if len(sys.argv) != 2:
|
|
8
|
+
print("사용법: python -m gglang 파일.ggya", file=sys.stderr)
|
|
9
|
+
sys.exit(2)
|
|
10
|
+
with open(sys.argv[1], encoding="utf-8") as f:
|
|
11
|
+
code = f.read()
|
|
12
|
+
try:
|
|
13
|
+
GGya().run(code)
|
|
14
|
+
except GGError as e:
|
|
15
|
+
sys.stdout.flush()
|
|
16
|
+
print(f"아 예? 엄 줴줴이야~ {e}", file=sys.stderr)
|
|
17
|
+
sys.exit(1)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
main()
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
# 원소: 엄(부호) 0개 이상 + 숫자/변수/입력
|
|
5
|
+
ATOM = re.compile(r"(엄*)((?:아뇨)+|쥐~+랄|\x01)")
|
|
6
|
+
INT_IN = "\x01" # 아 예?
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GGError(Exception):
|
|
10
|
+
def __init__(self, line_no, msg):
|
|
11
|
+
super().__init__(f"{line_no}번째 줄: {msg}")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GGya:
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.vars = {}
|
|
17
|
+
self.lines = []
|
|
18
|
+
self.pair = {}
|
|
19
|
+
self.outer = {}
|
|
20
|
+
self.no = 0 # 지금 보고 있는 줄 번호 (에러 메시지용)
|
|
21
|
+
|
|
22
|
+
def error(self, msg):
|
|
23
|
+
raise GGError(self.no, msg)
|
|
24
|
+
|
|
25
|
+
def type(self, line):
|
|
26
|
+
s = line.rstrip(" ")
|
|
27
|
+
if s == "":
|
|
28
|
+
return "EMPTY"
|
|
29
|
+
if re.fullmatch(r"요이~+", s):
|
|
30
|
+
return "LOOP"
|
|
31
|
+
if s == "줴줴이야" or s.startswith("줴줴이야 "):
|
|
32
|
+
return "BREAK"
|
|
33
|
+
if s == "티~원":
|
|
34
|
+
return "SPACE"
|
|
35
|
+
if s.startswith("티~원 "):
|
|
36
|
+
return "NUM"
|
|
37
|
+
if s == "티~~원":
|
|
38
|
+
return "NEWLINE"
|
|
39
|
+
if s.startswith("티~~원 "):
|
|
40
|
+
return "CHAR"
|
|
41
|
+
if re.match(r"쥐~+랄 ", s):
|
|
42
|
+
return "VAR"
|
|
43
|
+
return "UNKNOWN"
|
|
44
|
+
|
|
45
|
+
def check(self):
|
|
46
|
+
stack = []
|
|
47
|
+
for i, line in enumerate(self.lines):
|
|
48
|
+
self.no = i + 1
|
|
49
|
+
s = line.rstrip(" ")
|
|
50
|
+
t = self.type(line)
|
|
51
|
+
if t == "UNKNOWN":
|
|
52
|
+
if re.fullmatch(r"쥐~+랄", s):
|
|
53
|
+
self.error("대입할 값이 없음")
|
|
54
|
+
self.error(f"모르는 명령 '{s}'")
|
|
55
|
+
elif t == "LOOP":
|
|
56
|
+
n = s.count("~")
|
|
57
|
+
if stack and stack[-1][0] == n:
|
|
58
|
+
_, open_i = stack.pop()
|
|
59
|
+
self.pair[open_i] = i
|
|
60
|
+
self.pair[i] = open_i
|
|
61
|
+
elif any(m == n for m, _ in stack):
|
|
62
|
+
self.error(f"요이{'~' * n} 짝이 엇갈림")
|
|
63
|
+
else:
|
|
64
|
+
stack.append((n, i))
|
|
65
|
+
elif t == "BREAK":
|
|
66
|
+
self.outer[i] = stack[-1][1] if stack else None
|
|
67
|
+
if s != "줴줴이야":
|
|
68
|
+
self.syntax(s[5:])
|
|
69
|
+
elif t == "NUM":
|
|
70
|
+
self.syntax(s[4:])
|
|
71
|
+
elif t == "CHAR":
|
|
72
|
+
self.syntax(s[5:])
|
|
73
|
+
elif t == "VAR":
|
|
74
|
+
self.syntax(s.split(" ", 1)[1])
|
|
75
|
+
if stack:
|
|
76
|
+
n, open_i = stack[0]
|
|
77
|
+
self.no = open_i + 1
|
|
78
|
+
self.error(f"요이{'~' * n} 짝이 없음")
|
|
79
|
+
|
|
80
|
+
def syntax(self, expr):
|
|
81
|
+
"""식 구문만 검사한다. 틀리면 GGError."""
|
|
82
|
+
expr = expr.strip(" ")
|
|
83
|
+
if "\t" in expr:
|
|
84
|
+
self.error("탭은 들여쓰기에만 쓸 수 있음")
|
|
85
|
+
while expr.startswith("엄 "):
|
|
86
|
+
expr = expr[2:].strip(" ")
|
|
87
|
+
if expr in ("", "엄"):
|
|
88
|
+
self.error("'엄' 뒤에 값이 없음")
|
|
89
|
+
if re.search(r"아 예\?{2,}", expr):
|
|
90
|
+
self.error("'아 예' 뒤 물음표는 1개만")
|
|
91
|
+
expr = expr.replace("아 예?", INT_IN)
|
|
92
|
+
for term in expr.split(" "):
|
|
93
|
+
if term == "":
|
|
94
|
+
continue
|
|
95
|
+
if term == "엄":
|
|
96
|
+
self.error("부정 '엄'은 식 맨 앞에만 쓸 수 있음")
|
|
97
|
+
pos = 0
|
|
98
|
+
while pos < len(term):
|
|
99
|
+
m = ATOM.match(term, pos)
|
|
100
|
+
if m:
|
|
101
|
+
pos = m.end()
|
|
102
|
+
continue
|
|
103
|
+
rest = term[pos:].lstrip("엄")
|
|
104
|
+
if rest == "":
|
|
105
|
+
self.error("'엄' 뒤에 값이 없음")
|
|
106
|
+
self.error(f"식에 쓸 수 없는 글자 '{rest[0]}'")
|
|
107
|
+
|
|
108
|
+
def run(self, code):
|
|
109
|
+
self.lines = []
|
|
110
|
+
for raw in code.split("\n"):
|
|
111
|
+
raw = raw.removesuffix("\r")
|
|
112
|
+
self.lines.append(raw.lstrip(" \t"))
|
|
113
|
+
self.check()
|
|
114
|
+
i = 0
|
|
115
|
+
while i < len(self.lines):
|
|
116
|
+
self.no = i + 1
|
|
117
|
+
line = self.lines[i]
|
|
118
|
+
s = line.rstrip(" ")
|
|
119
|
+
t = self.type(line)
|
|
120
|
+
if t == "SPACE":
|
|
121
|
+
print(" ", end="")
|
|
122
|
+
elif t == "CHAR":
|
|
123
|
+
n = self.value(s[5:])
|
|
124
|
+
if not (0 <= n <= 0x10FFFF) or 0xD800 <= n <= 0xDFFF:
|
|
125
|
+
self.error(f"문자로 바꿀 수 없는 수 '{n}'")
|
|
126
|
+
print(chr(n), end="")
|
|
127
|
+
elif t == "NEWLINE":
|
|
128
|
+
print()
|
|
129
|
+
elif t == "NUM":
|
|
130
|
+
print(self.value(s[4:]), end="")
|
|
131
|
+
elif t == "VAR":
|
|
132
|
+
name, expr = s.split(" ", 1)
|
|
133
|
+
self.vars[name.count("~")] = self.value(expr)
|
|
134
|
+
elif t == "LOOP":
|
|
135
|
+
if self.pair[i] < i:
|
|
136
|
+
i = self.pair[i]
|
|
137
|
+
elif t == "BREAK":
|
|
138
|
+
expr = s[5:]
|
|
139
|
+
if expr == "" or self.value(expr) == 0:
|
|
140
|
+
if self.outer[i] is None:
|
|
141
|
+
return
|
|
142
|
+
i = self.pair[self.outer[i]]
|
|
143
|
+
i += 1
|
|
144
|
+
|
|
145
|
+
def read(self):
|
|
146
|
+
line = sys.stdin.readline()
|
|
147
|
+
if line == "":
|
|
148
|
+
self.error("입력이 없음")
|
|
149
|
+
return line.removesuffix("\n").removesuffix("\r")
|
|
150
|
+
|
|
151
|
+
def value(self, expr):
|
|
152
|
+
expr = expr.strip(" ")
|
|
153
|
+
if expr.startswith("엄 "):
|
|
154
|
+
return 1 if self.value(expr[2:]) == 0 else 0
|
|
155
|
+
expr = expr.replace("아 예?", INT_IN)
|
|
156
|
+
result = 1
|
|
157
|
+
for term in expr.split(" "):
|
|
158
|
+
if term == "":
|
|
159
|
+
continue
|
|
160
|
+
total = 0
|
|
161
|
+
for signs, atom in ATOM.findall(term):
|
|
162
|
+
if atom.startswith("쥐"):
|
|
163
|
+
n = self.vars.get(atom.count("~"), 0)
|
|
164
|
+
elif atom == INT_IN:
|
|
165
|
+
line = self.read()
|
|
166
|
+
if not re.fullmatch(r"[+-]?[0-9]+", line.strip(" ")):
|
|
167
|
+
self.error(f"정수가 아님 '{line}'")
|
|
168
|
+
n = int(line.strip(" "))
|
|
169
|
+
else:
|
|
170
|
+
n = atom.count("아뇨")
|
|
171
|
+
if len(signs) % 2:
|
|
172
|
+
n = -n
|
|
173
|
+
total += n
|
|
174
|
+
result *= total
|
|
175
|
+
return result
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gglang
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: 줴줴이랭: "줴줴이야~" 밈으로 만든 난해한 프로그래밍 언어 (엄랭 오마주)
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://gglang.jihwan.kim
|
|
7
|
+
Keywords: esolang,esoteric,korean,interpreter
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Topic :: Software Development :: Interpreters
|
|
10
|
+
Classifier: Natural Language :: Korean
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: license-file
|
|
15
|
+
|
|
16
|
+
# 줴줴이랭 (gglang)
|
|
17
|
+
|
|
18
|
+
"줴줴이야~" 밈으로 만든 난해한 프로그래밍 언어. [엄랭](https://github.com/rycont/umjunsik-lang) 오마주.
|
|
19
|
+
|
|
20
|
+
- 플레이그라운드: https://gglang.jihwan.kim
|
|
21
|
+
- 확장자: `.ggya`
|
|
22
|
+
|
|
23
|
+
## 설치
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install gglang
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 실행
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
gglang hello.ggya
|
|
33
|
+
# 또는
|
|
34
|
+
python -m gglang hello.ggya
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 문법 요약
|
|
38
|
+
|
|
39
|
+
| 코드 | 뜻 |
|
|
40
|
+
|---|---|
|
|
41
|
+
| `아뇨` | 1. `아뇨아뇨아뇨`는 3 |
|
|
42
|
+
| 붙여 쓰기 | 더하기. `아뇨아뇨쥐~랄` → 2 + 변수 |
|
|
43
|
+
| 띄어 쓰기 | 곱하기. `아뇨아뇨 아뇨아뇨아뇨` → 2 × 3 |
|
|
44
|
+
| `엄` | 바로 뒤 원소의 부호 뒤집기. `엄아뇨` → -1 |
|
|
45
|
+
| `쥐~랄`, `쥐~~랄`, … | 변수 (물결 수가 변수 번호) |
|
|
46
|
+
| `쥐~랄 식` | 대입 |
|
|
47
|
+
| `아 예?` | 정수 입력 |
|
|
48
|
+
| `티~원 식` | 숫자 출력 |
|
|
49
|
+
| `티~원` | 공백 출력 |
|
|
50
|
+
| `티~~원 식` | 유니코드 문자 하나 출력 |
|
|
51
|
+
| `티~~원` | 줄바꿈 |
|
|
52
|
+
| `요이~` … `요이~` | 반복 (물결 수가 같은 것끼리 짝) |
|
|
53
|
+
| `줴줴이야 식` | 식이 0이면 반복 탈출 |
|
|
54
|
+
|
|
55
|
+
## 예제
|
|
56
|
+
|
|
57
|
+
`GG` 출력:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
쥐~랄 아뇨아뇨아뇨아뇨아뇨아뇨아뇨아뇨 아뇨아뇨아뇨아뇨아뇨아뇨아뇨아뇨아뇨
|
|
61
|
+
쥐~랄 쥐~랄엄아뇨
|
|
62
|
+
티~~원 쥐~랄
|
|
63
|
+
티~~원 쥐~랄
|
|
64
|
+
티~~원
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 라이선스
|
|
68
|
+
|
|
69
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
gglang/__init__.py
|
|
5
|
+
gglang/__main__.py
|
|
6
|
+
gglang/runtime.py
|
|
7
|
+
gglang.egg-info/PKG-INFO
|
|
8
|
+
gglang.egg-info/SOURCES.txt
|
|
9
|
+
gglang.egg-info/dependency_links.txt
|
|
10
|
+
gglang.egg-info/entry_points.txt
|
|
11
|
+
gglang.egg-info/top_level.txt
|
|
12
|
+
tests/test_golden.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gglang
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gglang"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "줴줴이랭: \"줴줴이야~\" 밈으로 만든 난해한 프로그래밍 언어 (엄랭 오마주)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
keywords = ["esolang", "esoteric", "korean", "interpreter"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Topic :: Software Development :: Interpreters",
|
|
17
|
+
"Natural Language :: Korean",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://gglang.jihwan.kim"
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
gglang = "gglang.__main__:main"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools]
|
|
27
|
+
packages = ["gglang"]
|
gglang-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""골든 테스트: tests/golden/*.ggya 를 실행해서 기대 결과와 비교한다.
|
|
2
|
+
|
|
3
|
+
파일 규칙 (이름이 같은 파일끼리 한 세트)
|
|
4
|
+
- 이름.ggya : 실행할 프로그램
|
|
5
|
+
- 이름.in : 표준 입력 (없으면 빈 입력)
|
|
6
|
+
- 이름.out : 기대 표준 출력 (없으면 출력이 비어 있어야 함)
|
|
7
|
+
- 이름.err : 기대 에러 메시지 (있으면 종료 코드 1, 없으면 0)
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
17
|
+
GOLDEN = Path(__file__).resolve().parent / "golden"
|
|
18
|
+
CASES = sorted(p.stem for p in GOLDEN.glob("*.ggya"))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def read(path):
|
|
22
|
+
return path.read_text(encoding="utf-8") if path.exists() else None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.mark.parametrize("name", CASES)
|
|
26
|
+
def test_golden(name):
|
|
27
|
+
stdin = read(GOLDEN / f"{name}.in") or ""
|
|
28
|
+
want_out = read(GOLDEN / f"{name}.out") or ""
|
|
29
|
+
want_err = read(GOLDEN / f"{name}.err")
|
|
30
|
+
|
|
31
|
+
env = dict(os.environ, PYTHONIOENCODING="utf-8")
|
|
32
|
+
proc = subprocess.run(
|
|
33
|
+
[sys.executable, "-m", "gglang", str(GOLDEN / f"{name}.ggya")],
|
|
34
|
+
input=stdin.encode("utf-8"),
|
|
35
|
+
capture_output=True,
|
|
36
|
+
cwd=ROOT,
|
|
37
|
+
env=env,
|
|
38
|
+
timeout=5,
|
|
39
|
+
)
|
|
40
|
+
out = proc.stdout.decode("utf-8")
|
|
41
|
+
err = proc.stderr.decode("utf-8")
|
|
42
|
+
|
|
43
|
+
assert out == want_out, "표준 출력이 다름"
|
|
44
|
+
if want_err is None:
|
|
45
|
+
assert err == "", "에러가 나면 안 됨"
|
|
46
|
+
assert proc.returncode == 0, "종료 코드는 0이어야 함"
|
|
47
|
+
else:
|
|
48
|
+
assert err.rstrip("\n") == want_err.rstrip("\n"), "에러 메시지가 다름"
|
|
49
|
+
assert proc.returncode == 1, "에러 종료 코드는 1이어야 함"
|