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