slow.monster

STUDIO HAZE · jeniapp

Topaz source

Open by house policy. Topaz is our own language.

Topaz 5.20.2 · 63 files

shared/helpers.tpz

Topaz · 8969 B · Original file

1// 여러 도구가 함께 쓰는 도우미의 단일 원본입니다. 도구 소스의 공용 도우미 영역은 이 파일에서 생성합니다.
2// 함수 하나는 바로 앞의 주석 줄과 함께 한 덩어리로 복사됩니다. 덩어리 사이는 빈 줄 하나로 나눕니다.
3// Single source for helpers shared by several tools; each tool's helper region is generated from here.
5// 사용자 입력을 HTML 에 넣기 전에 반드시 거칩니다.
6function 이스케이프(원문: string) -> string {
7 원문.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;")
8}
10// 요소 본문에 넣는 사용자 입력에 씁니다. 큰따옴표를 그대로 두므로 속성 값에는 이스케이프를 씁니다.
11function 본문이스케이프(원문: string) -> string {
12 원문.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
15function 공백인가(글자: string) -> bool {
16 글자 == " " || 글자 == "\t" || 글자 == "\n" || 글자 == "\r"
19// 고정폭에서 두 칸을 차지하는 범위입니다. 한글 음절, 호환 자모, 자모, 한자, CJK 문장부호, 전각 영숫자 순입니다.
20// 코드포인트를 10진수로 적습니다. 토파즈 과제 '16진 정수 리터럴' 의 우회입니다.
21function 전각인가(글자: string) -> bool {
22 let 코드 = 글자.codePointAt(0) ?? 0
23 (코드 >= 44032 && 코드 <= 55203)
24 || (코드 >= 12592 && 코드 <= 12687)
25 || (코드 >= 4352 && 코드 <= 4607)
26 || (코드 >= 19968 && 코드 <= 40959)
27 || (코드 >= 12288 && 코드 <= 12351)
28 || (코드 >= 65280 && 코드 <= 65519)
31function 글자너비(글자: string) -> int {
32 if 전각인가(글자) { 2 } else { 1 }
35function 숫자인가(글자: string) -> bool {
36 let 코드 = 글자.codePointAt(0) ?? 0
37 코드 >= 48 && 코드 <= 57
40// 빈 문자열은 참입니다. 부호와 소수점을 받지 않으려고 toInt 앞에서 씁니다.
41function 모두숫자인가(글: string) -> bool {
42 글.scalars().reduce(true, (모두, 글자) => 모두 && 숫자인가(글자))
45// 문구는 고정 문장이거나 이미 이스케이프한 값이어야 합니다.
46function 오류알림(문구: string) -> string {
47 "<div class=\"err\">{문구}</div>"
50// 시작부터 맞는가가 참인 글자가 이어지는 끝 위치를 돌려줍니다. 맞는 글자가 없으면 시작 그대로입니다.
51function 연속끝(글자들: Array<string>, 시작: int, 맞는가: (string) -> bool) -> int {
52 let mut 끝 = 시작
53 while 끝 < 글자들.length && 맞는가(글자들[끝]) { 끝 += 1 }
54 끝
57// 위치가 글자들 끝을 넘으면 거짓입니다.
58function 글자인가(글자들: Array<string>, 위치: int, 글자: string) -> bool {
59 위치 < 글자들.length && 글자들[위치] == 글자
62// ASCII 소문자만 바꿉니다. 다른 문자의 대문자 규칙을 섞지 않습니다.
63function 대문자로(글: string) -> string {
64 글.scalars().map(글자 => {
65 let 코드 = 글자.codePointAt(0) ?? 0
66 if 코드 >= 97 && 코드 <= 122 { fromCodePoint(코드 - 32) ?? 글자 } else { 글자 }
67 }).join("")
70// 정수 읽기가 실패한 까닭입니다. 보여 줄 문구는 앱이 정합니다.
71type 정수오류 = "숫자아님" | "너무큼"
73// 맨 앞의 빼기 부호 하나만 받습니다. 더하기 부호와 소수점은 받지 않습니다.
74function 정수인가(토큰: string) -> bool {
75 let 글자들 = 토큰.scalars()
76 let 시작 = if 토큰.startsWith("-") { 1 } else { 0 }
77 글자들.length > 시작 && 모두숫자인가(글자들.slice(시작, 글자들.length).join(""))
80// 부호까지 센 글자 수를 최대자릿수로 막아 toInt 와 뒤의 계산이 넘치지 않게 합니다.
81function 정수읽기(토큰: string, 최대자릿수: int) -> Result<int, 정수오류> {
82 if !정수인가(토큰) { return Err("숫자아님") }
83 if 토큰.scalars().length > 최대자릿수 { return Err("너무큼") }
84 Ok(toInt(토큰) ?? 0)
87// 부호 없는 숫자만 받습니다. 빈 글은 0 입니다.
88function 자연수읽기(글: string, 최대자릿수: int) -> Result<int, 정수오류> {
89 if !모두숫자인가(글) { return Err("숫자아님") }
90 if 글.scalars().length > 최대자릿수 { return Err("너무큼") }
91 Ok(toInt(글) ?? 0)
94type 간선 = { 출발: int, 도착: int }
96// 순서는 Kahn 대기열 순서, 층은 최장 경로 길이입니다. 남은 진입차수가 0 보다 큰 노드는 순환 때문에 놓지 못한 노드입니다.
97type 위상정렬결과 = { 순서: Array<int>, 층들: Array<int>, 남은진입차수: Array<int> }
99// 한 번 만들어 두면 위상 정렬과 순환 검사가 노드마다 간선 전체를 다시 훑지 않습니다. 행 안의 순서는 간선 순서입니다.
100function 인접목록(노드수: int, 간선들: Array<간선>) -> Array<Array<int>> {
101 let mut 인접: Array<Array<int>> = [for i in 0..<노드수 => []]
102 for 연결 in 간선들 { 인접[연결.출발].push(연결.도착) }
103 인접
106function 위상정렬하기(인접: Array<Array<int>>) -> 위상정렬결과 {
107 let n = 인접.length
108 let mut 진입차수 = [for i in 0..<n => 0]
109 for 도착들 in 인접 {
110 for 도착 in 도착들 { 진입차수[도착] += 1 }
111 }
112 let mut 층들 = [for i in 0..<n => 0]
113 let mut 대기열 = [for i in 0..<n if 진입차수[i] == 0 => i]
114 let mut 머리 = 0
115 while 머리 < 대기열.length {
116 let 현재 = 대기열[머리]
117 머리 += 1
118 for 도착 in 인접[현재] {
119 진입차수[도착] -= 1
120 층들[도착] = 층들[도착].atLeast(층들[현재] + 1)
121 if 진입차수[도착] == 0 { 대기열.push(도착) }
122 }
123 }
124 { 순서: 대기열, 층들: 층들, 남은진입차수: 진입차수 }
127// 순환을 가리키기만 하는 노드도 위상 정렬에서 남으므로 길이 1 이상의 경로로 자기 자신에게 돌아오는지 따로 봅니다.
128function 순환소속인가(시작: int, 인접: Array<Array<int>>) -> bool {
129 let mut 방문: Set<int> = Set.of()
130 // 인접 목록의 행에 직접 넣지 않도록 복사해서 시작합니다.
131 let mut 대기열 = 인접[시작].slice(0, 인접[시작].length)
132 let mut 머리 = 0
133 while 머리 < 대기열.length {
134 let 현재 = 대기열[머리]
135 머리 += 1
136 if 현재 == 시작 { return true }
137 if !(현재 in 방문) {
138 방문.add(현재)
139 for 다음 in 인접[현재] { 대기열.push(다음) }
140 }
141 }
142 false
145// 같은 접두의 줄이 여럿이면 마지막 줄을 씁니다.
146function 줄값(입력: string, 접두: string) -> string {
147 let 맞는줄들 = 입력.split("\n").map(줄 => 줄.trim()).filter(줄 => 줄.startsWith(접두))
148 if 맞는줄들.length == 0 { return "" }
149 let 끝줄 = 맞는줄들[맞는줄들.length - 1]
150 끝줄.slice(접두.scalars().length, 끝줄.scalars().length).trim()
153// "키=값,키=값" 에서 = 로 정확히 두 조각인 항만 읽고 키와 값의 앞뒤 공백을 지웁니다. 같은 키가 여럿이면 마지막 값을 씁니다.
154function 셸값(셸: string, 키: string) -> string {
155 let 값들 = 셸.split(",").map(항 => 항.split("=")).filter(조각들 => 조각들.length == 2 && 조각들[0].trim() == 키)
156 if 값들.length == 0 { "" } else { 값들[값들.length - 1][1].trim() }
159// 곱수 1103515245, 증분 12345, 법 2^31 의 선형 합동입니다. 값이 2^31 미만이면 곱이 64비트를 넘지 않고, 음수는 [0, 2^31) 로 옮깁니다.
160function 선형합동다음(값: int) -> int {
161 let 다음 = (1103515245 * 값 + 12345) % 2147483648
162 if 다음 < 0 { 다음 + 2147483648 } else { 다음 }
165// 5381 에서 시작해 글자마다 33 을 곱해 더하는 djb2 해시입니다. 2147483647 로 나눈 나머지만 들고 가므로 int 범위를 넘지 않습니다.
166function djb2해시(글: string) -> int {
167 글.scalars().reduce(5381, (합, 글자) => (합 * 33 + (글자.codePointAt(0) ?? 0)) % 2147483647)
170// FNV 오프셋 기저에서 시작하는 31 배 다항 해시입니다. 글자마다 2^31 로 줄여 중간값이 2^31 * 31 을 넘지 않습니다.
171function 다항31해시(글: string) -> int {
172 글.scalars().reduce(2166136261 % 2147483648, (해시, 글자) => (해시 * 31 + (글자.codePointAt(0) ?? 0)) % 2147483648)
175// 성공 값과 안내가 모두 바로 보여 줄 HTML 조각일 때 진입점에서 한 번 풉니다. 토파즈 과제 'Result 오류 쪽 변환과 풀기' 의 우회입니다.
176function 결과풀기(값: Result<string, string>) -> string {
177 match 값 {
178 case Ok(결과) => 결과
179 case Err(안내) => 안내
180 }
183// 성공 값은 그대로 두고 오류만 문구로 바꿉니다. 토파즈 과제 'Result 오류 쪽 변환과 풀기' 의 우회입니다.
184function 오류바꾸기<T, E>(결과: Result<T, E>, 바꾸기: (E) -> string) -> Result<T, string> {
185 match 결과 {
186 case Ok(값) => Ok(값)
187 case Err(오류) => Err(바꾸기(오류))
188 }

SHA256 672488d37faa66940a697aea5bde4a9462f7e563a9218a117a18715f8381882a