@uiwwsw/hangulscript 0.7.0 → 0.8.0

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # 0.8.0
2
+
3
+ - 23개 페이지의 공식 문서: 검색·코드 복사·실행 링크·함수별 타입과 동작·SEO.
4
+ - 목록·집합·사전·복사·정렬·검증 등을 포함한 61개 한글 함수와 타입 별칭.
5
+ - 정렬·복사·중복 제거를 문장형 값으로 표현하는 문법.
6
+ - ㅎ 파일 아이콘과 표준 기능을 제공하는 VS Code 확장 0.3.0.
7
+ - GitHub Actions NPM_TOKEN 기반 릴리스 게시 워크플로.
8
+
1
9
  # 0.7.0
2
10
 
3
11
  - 공식 VS Code 확장 0.2.0: 문법 강조·포맷·진단·타입 정보·정의 이동·스코프 자동 완성·스니펫.
package/LANGUAGE.md CHANGED
@@ -135,7 +135,7 @@ React·Vue의 Vite 플러그인은 `.hgs` import를 빌드할 때 처리합니
135
135
 
136
136
  자료형·타입 별칭·열거값은 최상위에 작성합니다. 클래스의 추상·접근 제한자·데코레이터 및 전체 TypeScript 문법은 지원 범위가 아닙니다.
137
137
 
138
- ## 기본 함수 15개
138
+ ## 기본 함수와 표준 기능
139
139
 
140
140
  | 함수 | 의미 |
141
141
  | --- | --- |
@@ -170,3 +170,11 @@ React·Vue의 Vite 플러그인은 `.hgs` import를 빌드할 때 처리합니
170
170
  웹 데모와 Vite 플러그인은 문법 변환·JS 구문 진단을 제공하며 전체 타입 검사는 하지 않습니다.
171
171
  문장 오류는 원본 행 번호를 표시합니다. TypeScript 타입 진단은 생성 코드 위치를 사용하며 원본 소스맵은 아직 제공하지 않습니다.
172
172
  임의의 한국어를 해석하는 AI 실행기·Python 호환 언어·전체 JavaScript/TypeScript 대체 언어가 아닙니다.
173
+
174
+ ## 목록·집합·사전과 문장형 값
175
+
176
+ 0.8.0은 목록·집합·사전·복사·정렬을 포함해 61개 한글 함수를 제공합니다. 타입 자리에서 `목록<T>`는 `Array<T>`, `집합<T>`는 `Set<T>`, `사전<K, V>`는 `Map<K, V>`입니다. `목록으로(값)`으로 순회 가능한 값을 배열로 바꿉니다.
177
+
178
+ 값 자리에서 「대상을 오름차순으로 정렬한 값」「대상을 내림차순으로 정렬한 값」「대상을 얕게 복사한 값」「대상을 깊게 복사한 값」「대상에서 중복을 제거한 값」을 사용할 수 있습니다. 정해진 문법이며 임의의 산문을 추측하지 않습니다.
179
+
180
+ [자료구조 안내](https://hangulscript.vercel.app/docs/collections/) · [전체 표준 기능](https://hangulscript.vercel.app/docs/standard-library/)
package/README.md CHANGED
@@ -270,7 +270,7 @@ HTML·이벤트 핸들러 문자열을 삽입하지 않습니다.
270
270
  **0.6.0 이상**의 공통 Vite 플러그인을 사용합니다. React/Vue의 화면과 상태 관리에 한글스크립트 계산·검증 함수를 연결할 수 있습니다.
271
271
 
272
272
  ```bash
273
- npm install --save-dev @uiwwsw/hangulscript@^0.7.0
273
+ npm install --save-dev @uiwwsw/hangulscript@^0.8.0
274
274
  ```
275
275
 
276
276
  ```js
@@ -315,3 +315,19 @@ console.log(결제금액(10000, 20)); // 8000
315
315
  Node에서 직접 실행하려면 `node --import @uiwwsw/hangulscript/register src/main.hgs`를 사용합니다. `ksc run`도 같은 로더를 사용하며 상대 경로 `.hgs` import를 처리합니다. `ksc build`는 진입 폴더 아래 HGS 모듈을 함께 빌드하고 import를 `.js`로 연결합니다.
316
316
 
317
317
  [환경별 지원 계약·타입 설정·호환성 정책](docs/support.md)을 확인하세요. 언어 패키지와 VS Code 확장은 같은 저장소에서 검증하며 별도 버전으로 배포합니다.
318
+
319
+ ## 문서와 자료구조
320
+
321
+ [공식 문서](https://hangulscript.vercel.app/docs/)는 시작하기·문법·자료구조·모듈·프레임워크·편집기·오류 해결을 페이지별로 제공합니다. [61개 표준 함수](https://hangulscript.vercel.app/docs/standard-library/)에는 타입·실행 예제·원본 변경 여부가 있습니다.
322
+
323
+ ```text
324
+ 점수들은 [88, 92, 88]이다
325
+ 점수순은 점수들을 내림차순으로 정렬한 값이다
326
+ 고유값은 점수들에서 중복을 제거한 값이다
327
+ 독립본은 점수들을 깊게 복사한 값이다
328
+ 출력(점수순, 고유값, 독립본)
329
+ ```
330
+
331
+ 목록의 변환·검색·누적·펼치기, 집합 연산, 사전(Map), 얕은·깊은 복사, 안정적인 기준 정렬을 제공합니다. 전체 JS API를 한글화한 상태는 아니며 [지원 범위](docs/support.md)에 제약을 명시합니다. [자동 게시 설정](docs/publishing.md)은 GitHub Actions의 `NPM_TOKEN` 시크릿을 사용합니다.
332
+
333
+ [한글스크립트의 큰 그림과 로드맵](https://hangulscript.vercel.app/docs/roadmap/)에서 현재 지원·부분 지원·미구현과 다음 개발 순서를 확인하세요.
@@ -0,0 +1,57 @@
1
+ export const collectionsRuntime = `
2
+ function 목록으로<T>(값: Iterable<T>): T[] { return Array.from(값); }
3
+ function 첫번째<T>(값: readonly T[]): T | undefined { return 값[0]; }
4
+ function 마지막<T>(값: readonly T[]): T | undefined { return 값.at(-1); }
5
+ function 꺼내기<T>(값: readonly T[], 순서: number): T | undefined { return 값.at(순서); }
6
+ function 각각바꾸기<T, U>(값: readonly T[], 바꿀: (항목: T, 순서: number) => U): U[] { return 값.map(바꿀); }
7
+ function 골라내기<T>(값: readonly T[], 조건: (항목: T, 순서: number) => boolean): T[] { return 값.filter(조건); }
8
+ function 찾기<T>(값: readonly T[], 조건: (항목: T, 순서: number) => boolean): T | undefined { return 값.find(조건); }
9
+ function 순서찾기<T>(값: readonly T[], 조건: (항목: T, 순서: number) => boolean): number { return 값.findIndex(조건); }
10
+ function 모두만족<T>(값: readonly T[], 조건: (항목: T) => boolean): boolean { return 값.every(조건); }
11
+ function 하나라도만족<T>(값: readonly T[], 조건: (항목: T) => boolean): boolean { return 값.some(조건); }
12
+ function 누적하기<T, U>(값: readonly T[], 시작: U, 합칠: (누적: U, 항목: T, 순서: number) => U): U { return 값.reduce(합칠, 시작); }
13
+ function 잘라내기<T>(값: readonly T[], 시작: number, 끝?: number): T[] { return 값.slice(시작, 끝); }
14
+ function 이어붙이기<T>(...목록들: readonly (readonly T[])[]): T[] { return 목록들.flatMap(값 => [...값]); }
15
+ function 뒤집기<T>(값: readonly T[]): T[] { return [...값].reverse(); }
16
+ function 펼치기<T>(값: readonly (readonly T[])[]): T[] { return 값.flatMap(항목 => [...항목]); }
17
+ function 나눠묶기<T>(값: readonly T[], 크기: number): T[][] {
18
+ if (!Number.isSafeInteger(크기) || 크기 < 1) throw new Error("묶음 크기는 1 이상의 안전한 정수여야 합니다.");
19
+ const 결과: T[][] = []; for (let 순서 = 0; 순서 < 값.length; 순서 += 크기) 결과.push(값.slice(순서, 순서 + 크기)); return 결과;
20
+ }
21
+ function 짝짓기<T, U>(가: readonly T[], 나: readonly U[]): [T, U][] { return 가.slice(0, 나.length).map((값, 순서) => [값, 나[순서]]); }
22
+ function 중복제거<T>(값: Iterable<T>): T[] { return [...new Set(값)]; }
23
+ function 집합<T>(값: Iterable<T> = []): Set<T> { return new Set(값); }
24
+ function 집합에추가<T>(값: Set<T>, 항목: T): Set<T> { return 값.add(항목); }
25
+ function 집합에서삭제<T>(값: Set<T>, 항목: T): boolean { return 값.delete(항목); }
26
+ function 합집합<T>(가: ReadonlySet<T>, 나: ReadonlySet<T>): Set<T> { const 결과 = new Set(가); for (const 값 of 나) 결과.add(값); return 결과; }
27
+ function 교집합<T>(가: ReadonlySet<T>, 나: ReadonlySet<T>): Set<T> { const 결과 = new Set<T>(); for (const 값 of 가) if (나.has(값)) 결과.add(값); return 결과; }
28
+ function 차집합<T>(가: ReadonlySet<T>, 나: ReadonlySet<T>): Set<T> { const 결과 = new Set<T>(); for (const 값 of 가) if (!나.has(값)) 결과.add(값); return 결과; }
29
+ function 대칭차집합<T>(가: ReadonlySet<T>, 나: ReadonlySet<T>): Set<T> { const 결과 = new Set(가); for (const 값 of 나) { if (가.has(값)) 결과.delete(값); else 결과.add(값); } return 결과; }
30
+ function 부분집합인가<T>(가: ReadonlySet<T>, 나: ReadonlySet<T>): boolean { for (const 값 of 가) if (!나.has(값)) return false; return true; }
31
+ function 사전<K, V>(항목: Iterable<readonly [K, V]> = []): Map<K, V> { return new Map(항목); }
32
+ function 사전에담기<K, V>(대상: Map<K, V>, 키: K, 값: V): Map<K, V> { 대상.set(키, 값); return 대상; }
33
+ function 사전에서읽기<K, V>(대상: ReadonlyMap<K, V>, 키: K): V | undefined { return 대상.get(키); }
34
+ function 사전에있는가<K, V>(대상: ReadonlyMap<K, V>, 키: K): boolean { return 대상.has(키); }
35
+ function 사전에서삭제<K, V>(대상: Map<K, V>, 키: K): boolean { return 대상.delete(키); }
36
+ function 키목록<K, V>(대상: ReadonlyMap<K, V>): K[] { return [...대상.keys()]; }
37
+ function 값목록<K, V>(대상: ReadonlyMap<K, V>): V[] { return [...대상.values()]; }
38
+ function 항목목록<K, V>(대상: ReadonlyMap<K, V>): [K, V][] { return [...대상.entries()]; }
39
+ function 기준으로묶기<T, K>(값: readonly T[], 기준: (항목: T) => K): Map<K, T[]> {
40
+ const 결과 = new Map<K, T[]>(); for (const 항목 of 값) { const 키 = 기준(항목); const 묶음 = 결과.get(키); if (묶음) 묶음.push(항목); else 결과.set(키, [항목]); } return 결과;
41
+ }
42
+ function 얕게복사<T extends object>(값: T): T {
43
+ if (Array.isArray(값)) return [...값] as T;
44
+ if (값 instanceof Map) return new Map(값) as T;
45
+ if (값 instanceof Set) return new Set(값) as T;
46
+ if (Object.getPrototypeOf(값) === Object.prototype || Object.getPrototypeOf(값) === null) return Object.assign(Object.create(Object.getPrototypeOf(값)), 값) as T;
47
+ throw new Error("얕게복사는 목록·집합·사전·일반 객체에 사용하세요.");
48
+ }
49
+ function 깊게복사<T>(값: T): T { return structuredClone(값); }
50
+ function 오름차순<T extends number | string>(값: readonly T[]): T[] { return 정렬(값); }
51
+ function 내림차순<T extends number | string>(값: readonly T[]): T[] { for (const 항목 of 값) 정렬비교(항목, 항목); return 정렬(값, (가, 나) => -정렬비교(가, 나)); }
52
+ function 기준정렬<T>(값: readonly T[], 기준: (항목: T) => number | string, 방향: "오름차순" | "내림차순" = "오름차순"): T[] {
53
+ if (방향 !== "오름차순" && 방향 !== "내림차순") throw new Error("정렬 방향은 오름차순 또는 내림차순이어야 합니다.");
54
+ return 값.map(항목 => (() => { const 키 = 기준(항목); 정렬비교(키, 키); return { 항목, 키 }; })()).sort((가, 나) => 정렬비교(가.키, 나.키) * (방향 === "오름차순" ? 1 : -1)).map(항목 => 항목.항목);
55
+ }
56
+ function 확인한다(조건: unknown, 설명 = "확인 조건을 만족하지 못했습니다."): asserts 조건 { if (!조건) throw new Error(설명); }
57
+ `;
package/dist/editor.js CHANGED
@@ -30,7 +30,7 @@ export function inspectSource(source, filePath, line, column, settings = {}) {
30
30
  const checker = analysis.program.getTypeChecker();
31
31
  target = matched ?? target;
32
32
  const completions = checker.getSymbolsInScope(target, ts.SymbolFlags.Value | ts.SymbolFlags.Type | ts.SymbolFlags.Alias)
33
- .filter(symbol => symbol.declarations?.some(d => analysis.documents.has(d.getSourceFile().fileName) && !['화면연결'].includes(symbol.name)))
33
+ .filter(symbol => symbol.declarations?.some(d => analysis.documents.has(d.getSourceFile().fileName) && !['화면연결', '정렬비교'].includes(symbol.name)))
34
34
  .map(symbol => ({ name: symbol.name, detail: checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol,target)) }));
35
35
  if (!matched) return { completions, definitions: [] };
36
36
  let symbol = checker.getSymbolAtLocation(matched);
@@ -1,7 +1,7 @@
1
1
  import ts from 'typescript';
2
2
 
3
3
  const literals = new Map([['참', 'true'], ['거짓', 'false'], ['비어있음', 'null'], ['정의안됨', 'undefined'], ['자신', 'this'], ['부모', 'super']]);
4
- const types = new Map([['숫자', 'number'], ['문자열', 'string'], ['논리', 'boolean'], ['불리언', 'boolean'], ['아무거나', 'any'], ['모름', 'unknown'], ['무효', 'void']]);
4
+ const types = new Map([['숫자', 'number'], ['문자열', 'string'], ['논리', 'boolean'], ['불리언', 'boolean'], ['아무거나', 'any'], ['모름', 'unknown'], ['무효', 'void'], ['목록', 'Array'], ['집합', 'Set'], ['사전', 'Map']]);
5
5
 
6
6
  function identifiers(source, replacements) {
7
7
  const file = ts.createSourceFile('expression.ts', source, ts.ScriptTarget.Latest, true);
@@ -21,6 +21,10 @@ function identifiers(source, replacements) {
21
21
  }
22
22
 
23
23
  export function expression(source) {
24
+ let phrase;
25
+ if ((phrase = source.trim().match(/^(.+?)(?:을|를) (오름차순|내림차순)으로 정렬한 값$/u))) return phrase[2] + '(' + expression(phrase[1]) + ')';
26
+ if ((phrase = source.trim().match(/^(.+?)(?:을|를) (얕게|깊게) 복사한 값$/u))) return (phrase[2] === '얕게' ? '얕게복사' : '깊게복사') + '(' + expression(phrase[1]) + ')';
27
+ if ((phrase = source.trim().match(/^(.+?)에서 중복을 제거한 값$/u))) return '중복제거(' + expression(phrase[1]) + ')';
24
28
  source = source.replace(/^새\s+/, 'new ').replace(/^기다리기\s+/, 'await ');
25
29
  if (/[참거비정자부]/.test(source)) {
26
30
  const prefix = 'const __expression = (';
package/dist/runtime.js CHANGED
@@ -1,10 +1,11 @@
1
+ import { collectionsRuntime } from './collections.js';
1
2
  import { uiRuntime } from './ui.js';
2
3
  export const sentenceRuntime = `${uiRuntime}
3
4
 
4
5
  function 출력(...값: unknown[]): void { console.log(...값); }
5
- function 길이(값: string | readonly unknown[]): number { return 값.length; }
6
+ function 길이(값: string | readonly unknown[] | ReadonlySet<unknown> | ReadonlyMap<unknown, unknown>): number { return "size" in Object(값) ? (값 as ReadonlySet<unknown>).size : (값 as string | readonly unknown[]).length; }
6
7
  function 추가<T>(목록: T[], 값: T): void { 목록.push(값); }
7
- function 포함<T>(목록: readonly T[], 값: T): boolean { return 목록.includes(값); }
8
+ function 포함<T>(목록: readonly T[] | ReadonlySet<T>, 값: T): boolean { return 목록 instanceof Set ? 목록.has(값) : (목록 as readonly T[]).includes(값); }
8
9
  function 나누기(글: string, 구분: string): string[] { return 글.split(구분); }
9
10
  function 잇기(목록: readonly unknown[], 구분 = ""): string { return 목록.join(구분); }
10
11
  function 합계(값: readonly number[]): number { return 값.reduce((합, 항목) => 합 + 항목, 0); }
@@ -33,7 +34,17 @@ function 최댓값(목록: readonly number[]): number {
33
34
  let 결과 = 목록[0]; for (const 항목 of 목록) if (항목 > 결과) 결과 = 항목;
34
35
  return 결과;
35
36
  }
36
- function 정렬(값: readonly number[]): number[] { return [...값].sort((가, 나) => 가 - 나); }
37
+ function 정렬비교(가: number | string, 나: number | string): number {
38
+ if (typeof 가 !== typeof 나) throw new Error("같은 종류의 숫자 또는 문자열을 정렬하세요.");
39
+ if (typeof 가 === "number" && typeof 나 === "number") { if (!Number.isFinite(가) || !Number.isFinite(나)) throw new Error("정렬할 숫자는 유한해야 합니다."); return 가 - 나; }
40
+ return (가 as string).localeCompare(나 as string, "ko", { numeric: true });
41
+ }
42
+ function 정렬<T extends number | string>(값: readonly T[]): T[];
43
+ function 정렬<T>(값: readonly T[], 비교: (가: T, 나: T) => number): T[];
44
+ function 정렬<T>(값: readonly T[], 비교?: (가: T, 나: T) => number): T[] {
45
+ if (!비교 && 값.some(항목 => !["number", "string"].includes(typeof 항목) || typeof 항목 === "number" && !Number.isFinite(항목))) throw new Error("정렬할 값은 유한한 숫자 또는 문자열이어야 합니다.");
46
+ return [...값].sort(비교 ?? ((가, 나) => 정렬비교(가 as number | string, 나 as number | string)));
47
+ }
37
48
  function 범위(시작: number, 끝?: number, 간격 = 1): number[] {
38
49
  if (끝 === undefined) { 끝 = 시작; 시작 = 0; }
39
50
  if (!Number.isFinite(시작) || !Number.isFinite(끝) || !Number.isFinite(간격) || 간격 === 0) throw new Error("범위에는 유한한 수와 0이 아닌 간격이 필요합니다.");
@@ -41,4 +52,5 @@ function 범위(시작: number, 끝?: number, 간격 = 1): number[] {
41
52
  if (개수 > 100000) throw new Error("범위는 최대 100000개까지 만들 수 있습니다.");
42
53
  return Array.from({ length: 개수 }, (_, 순서) => 시작 + 순서 * 간격);
43
54
  }
55
+ ${collectionsRuntime}
44
56
  `;
@@ -0,0 +1,45 @@
1
+ # 한글스크립트의 큰 그림
2
+
3
+ 한글 문장으로 프로그램의 의도를 분명히 쓰고, JavaScript 생태계에서 실행하는 언어를 만듭니다. 현재의 중심은 **문장형 소스 → 타입 검사 → JavaScript + 타입 선언**입니다. 이 문서는 완료 보고가 아니라 현재 지원 범위와 이후 개발의 기준입니다. 기능 추가만으로 1.0을 선언하지 않습니다.
4
+
5
+ <div class="architecture" role="group" aria-label="한글스크립트 컴파일과 실행 구조"><div><b>01</b><strong>한글 문장 .hgs</strong><span>이름 · 조건 · 행동 · 결과</span></div><div><b>02</b><strong>문법과 들여쓰기</strong><span>정해진 문장 규칙 해석</span></div><div><b>03</b><strong>타입과 오류 검사</strong><span>TypeScript 검사기 활용</span></div><div><b>04</b><strong>JS + .d.ts</strong><span>실행 모듈과 타입 계약</span></div><div><b>05</b><strong>Node · 웹</strong><span>Vite · React · Vue 연결</span></div><div><b>06</b><strong>VS Code · 문서</strong><span>작성 · 이해 · 수정 지원</span></div></div>
6
+
7
+ ## 지금의 기반
8
+
9
+ | 계층 | 현재 제공 | 부분 지원·한계 | 다음 개발 |
10
+ | --- | --- | --- | --- |
11
+ | 문법 | 문장형 선언·함수·분기·반복·모듈·예외·비동기·클래스·타입 | 정해진 문장만 해석. 전체 JS/TS 소스 호환 아님 | 전용 AST, 문법 오류 복구, 더 정밀한 토큰 위치 |
12
+ | 타입 | TypeScript strict 기본 검사·제네릭·타입 선언 출력 | any·빈 목록의 검사 제한, 변환 토큰 열은 근사할 수 있음 | 더 나은 기본 추론, 문장별 진단, 명시적인 안전성 규칙 |
13
+ | 표준 기능 | 61개 한글 함수: 목록·집합·사전·복사·정렬·숫자·문자열·검증·화면 | 모든 내장 JS API의 한글 별칭은 아님. 일부 원본 변경 함수 존재 | 객체·문자열·날짜 확장, 표준 모듈 분리, 이름 충돌 완화 |
14
+ | 실행과 빌드 | Node 등록 로더·CLI·ESM·JS + .d.ts·Vite | 진입 폴더 밖 HGS 의존성 빌드와 자산 복사 미지원 | 프로젝트 빌드·증분 컴파일·watch·정밀한 소스맵 |
15
+ | 편집기 | 포맷·진단·타입 정보·정의 이동·스코프 자동 완성·스니펫·ㅎ 아이콘 | 전체 속성 완성·자동 import·참조 찾기·이름 변경·디버거 미지원 | LSP로 기능 분리, 증분 분석, 안전한 리팩터링 |
16
+ | 신뢰성 | 문법·실행·타입·문서 예제·프레임워크·실제 편집기 테스트 | 테스트 통과는 모든 입력의 올바름을 뜻하지 않음 | 명세 기반 적합성 테스트·퍼징·회귀·실측 성능 기준 |
17
+ | 문서와 배포 | 버전별 변경 내역·함수 계약·예제·npm·Marketplace·CI | 0.x에서 문법과 API가 바뀔 수 있음 | 마이그레이션 도구·릴리스 규칙·1.0 호환성 약속 |
18
+
19
+ ## 순서대로 만들기
20
+
21
+ ### 1. 지금 있는 언어를 믿고 사용할 수 있게
22
+
23
+ 함수마다 입력·반환·원본 변경·실패 조건을 일관되게 문서화하고 예제를 실행해 검사합니다. 잘못된 입력과 오류 위치를 회귀 테스트로 남깁니다. 문장형의 모든 조합을 지원한다고 가정하지 않고 지원·부분 지원·미구현을 공개합니다. 현재 0.8 작업은 문서·자료구조·아이콘·배포에 집중합니다.
24
+
25
+ ### 2. 언어 내부를 확장하기 좋은 구조로
26
+
27
+ 현재 문장·들여쓰기는 규칙 기반 파서로 처리하고 표현식에는 TypeScript AST를 활용합니다. 문장 전체를 나타내는 전용 AST와 범위 정보를 도입한 뒤 문법 검사·타입 연결·출력·포맷을 분리합니다. 이를 기반으로 오류를 여러 개 복구해서 보여주고, 정확한 소스맵과 프로젝트 단위 증분 분석을 만듭니다.
28
+
29
+ ### 3. 실용적인 개발 환경을 완성하기
30
+
31
+ 편집기의 전체 속성 자동 완성·자동 import·참조와 이름 변경을 LSP에 연결합니다. 프로젝트 설정·증분 빌드·테스트 명령을 제공하고 React/Vue에서 안정적으로 쓰는 예제를 유지합니다. 파일·네트워크·날짜·객체 등은 Node와 브라우저의 권한 차이를 구분한 선택 모듈로 설계합니다.
32
+
33
+ ### 4. 문장이 탄탄할수록 검사할 수 있는 내용 늘리기
34
+
35
+ 이름과 타입은 등장인물과 사물을, 사전·사후 조건은 세계의 규칙을, 함수와 모듈은 사건과 장면을 표현합니다. 현재는 `확인한다`로 실행 중 조건을 검사할 수 있습니다. 다음으로 입력 조건·결과 조건·예제 테스트를 더 읽기 쉬운 문장으로 선언하는 방식을 설계할 수 있습니다. 효과가 있는 문법인지 작은 실험과 실제 예제로 먼저 검증합니다.
36
+
37
+ ### 5. 1.0을 위한 기준 정하기
38
+
39
+ 문법 명세와 표준 API를 고정하고 호환성·폐기 정책을 확정합니다. 핵심 오류 사례, 적합성 테스트, 편집기 기능, 선언 출력, Node·브라우저·프레임워크 연동을 릴리스 기준으로 정합니다. 성능은 환경과 입력 크기를 함께 공개하고 회귀를 추적합니다. 완료되지 않은 항목이 있으면 이름을 붙여 공개합니다.
40
+
41
+ ## AI와 별도 실행 엔진의 위치
42
+
43
+ 핵심 문법·타입·컴파일·조건 검사에는 AI API가 필요하지 않습니다. 자유로운 산문에서 의도를 추출하는 기능은 선택적인 작성 보조로 실험할 수 있고 로컬 모델도 가능합니다. 생성한 결과도 같은 검사·테스트를 통과해야 합니다. 현재 자유 산문 실행, AI 기반 문장 해석, 자동 정형 증명은 제공하지 않습니다.
44
+
45
+ JS + .d.ts 경로는 기존 생태계와 타입을 재사용하는 현재의 실행 계약입니다. 독립 VM·바이트코드·네이티브 출력은 별도의 큰 프로젝트이므로 실제 요구와 측정 결과가 생기면 검토합니다. 정식 문서와 지원 도구를 갖추는 데 독립 실행 엔진이 필수인 것은 아닙니다.
@@ -1,6 +1,6 @@
1
1
  # React·Vue에서 한글스크립트 사용하기
2
2
 
3
- 이 가이드는 `@uiwwsw/hangulscript@0.7.0` 문법과 공통 Vite 플러그인을 사용합니다.
3
+ 이 가이드는 `@uiwwsw/hangulscript@0.8.0` 문법과 공통 Vite 플러그인을 사용합니다.
4
4
  화면과 상태는 React/Vue로 만들고 계산·조건·검증 함수를 `.hgs`에 작성합니다.
5
5
  데모의 `숫자입력`·`버튼`·`보여주기`는 데모 UI 실행 환경의 API이며 React/Vue 컴포넌트로 자동 변환되지 않습니다.
6
6
 
@@ -26,7 +26,7 @@ Vue는 같은 저장소의 `examples/vue-vite`에서 `npm ci`와 `npm run dev`
26
26
  ## 기존 Vite 프로젝트에 설치하기
27
27
 
28
28
  ```bash
29
- npm install --save-dev @uiwwsw/hangulscript@^0.7.0
29
+ npm install --save-dev @uiwwsw/hangulscript@^0.8.0
30
30
  ```
31
31
 
32
32
  React/Vue와 해당 Vite 플러그인은 기존 프로젝트에 설치돼 있다고 가정합니다.
@@ -0,0 +1,61 @@
1
+ # 목록·집합·사전 고르기
2
+
3
+ 배열은 순서를 가진 **목록**, Set은 중복 없는 **집합**, Map은 키와 값을 연결하는 **사전**으로 표현합니다. 값과 목적에 맞춰 자료구조를 고르면 코드의 뜻이 더 명확해집니다.
4
+
5
+ ```hgs
6
+ 점수들은 [75, 92, 88, 92]이다
7
+ 통과점수는 골라내기(점수들, 점수 => 점수 >= 80)이다
8
+ 서로다른점수는 집합(통과점수)이다
9
+ 출력(내림차순(목록으로(서로다른점수)))
10
+ ```
11
+
12
+ `배열`을 나타내는 타입은 `목록<숫자>` 또는 `숫자[]`입니다. 집합은 `집합<문자열>`, 사전은 `사전<문자열, 숫자>`로 적습니다. 함수의 매개변수에 타입을 쓰면 잘못된 값이 들어오는 호출을 빌드 단계에서 찾을 수 있습니다.
13
+
14
+ ```hgs
15
+ 재고: 사전<문자열, 숫자>는 사전()이다
16
+ 사전에담기(재고, "사과", 3)
17
+ 사전에담기(재고, "배", 5)
18
+ 출력(키목록(재고), 사전에서읽기(재고, "사과"))
19
+ ```
20
+
21
+ ## 원본과 복사본
22
+
23
+ `정렬`, `오름차순`, `내림차순`, `골라내기`, `각각바꾸기`, `뒤집기`, `잘라내기`는 원본 배열을 바꾸지 않습니다. `추가`, `집합에추가`, `집합에서삭제`, `사전에담기`, `사전에서삭제`는 원본을 바꿉니다. 함수별 참조 문서에서 확인하세요.
24
+
25
+ 얕은 복사는 바깥 그릇만 새로 만듭니다. 안쪽 객체는 공유합니다. 깊은 복사는 복제 가능한 안쪽 값까지 별도로 만듭니다.
26
+
27
+ ```hgs
28
+ 원본은 [{ 점수: 10 }]이다
29
+ 얕은복사본은 얕게복사(원본)이다
30
+ 깊은복사본은 깊게복사(원본)이다
31
+ 얕은복사본[0].점수 = 20
32
+ 출력(원본[0].점수, 깊은복사본[0].점수)
33
+ ```
34
+
35
+ 결과는 `20 10`입니다. 깊은 복사는 함수·DOM 노드 등 복제 불가능한 값에 오류를 냅니다. 객체를 중복 제거하거나 집합에 넣을 때에는 내용이 같아도 참조가 다르면 서로 다른 값입니다.
36
+
37
+ ## 정렬 기준 쓰기
38
+
39
+ ```hgs
40
+ 학생들은 [{ 이름: "하늘", 점수: 88 }, { 이름: "바다", 점수: 92 }]이다
41
+ 점수순은 기준정렬(학생들, 학생 => 학생.점수, "내림차순")이다
42
+ 출력(점수순)
43
+ ```
44
+
45
+ 숫자는 크기로, 문자열은 한국어 locale의 자연 정렬로 비교합니다. 같은 키의 항목은 원래 순서를 유지합니다. 더 복잡한 순서는 `정렬(목록, 비교함수)`로 표현하며 비교 함수는 음수·0·양수를 반환해야 합니다.
46
+
47
+ ## 비용을 알고 쓰기
48
+
49
+ 목록 변환·필터·복사는 항목 수에 비례한 시간과 새 배열 메모리가 필요합니다. 정렬은 일반적으로 O(n log n), 집합·사전 검색은 평균적으로 상수 시간에 가깝습니다. 깊은 복사는 중첩 데이터 전체의 시간·메모리를 사용합니다. `기준정렬`은 기준 함수를 항목마다 한 번만 호출합니다. 큰 반복에서 불필요하게 복사하지 말고, 2초 제한의 웹 데모와 일반 Node 실행을 구분하세요.
50
+
51
+ ## 문장처럼 적기
52
+
53
+ 정렬·복사·중복 제거는 값 자리에서 다음 문장으로도 적을 수 있습니다. 함수 호출과 같은 결과이며, 정해진 표현만 지원합니다.
54
+
55
+ ```hgs
56
+ 점수들은 [88, 92, 88]이다
57
+ 점수순은 점수들을 내림차순으로 정렬한 값이다
58
+ 서로다른값은 점수들에서 중복을 제거한 값이다
59
+ 독립본은 점수들을 깊게 복사한 값이다
60
+ 출력(점수순, 서로다른값, 독립본)
61
+ ```
@@ -0,0 +1,24 @@
1
+ # 오류 읽고 고치기
2
+
3
+ 문법·타입 오류는 원본 `.hgs`의 파일 이름·행·열로 표시됩니다. VS Code에서는 Problems 패널과 밑줄, Vite에서는 개발 화면의 오류와 실패한 빌드, CLI에서는 실패 종료 코드로 확인합니다. 웹 데모의 오류 항목을 누르면 해당 위치로 이동합니다.
4
+
5
+ ```text
6
+ main.hgs:1:1 [TS2322] 선언한 타입과 대입한 값의 타입을 확인하세요.
7
+ ```
8
+
9
+ `값: 숫자는 "문자"이다`는 숫자 타입과 문자열 값이 달라 실패합니다. `값: 숫자는 42이다`처럼 고치세요.
10
+
11
+ | 표시 | 뜻 | 확인할 내용 |
12
+ | --- | --- | --- |
13
+ | HGS1001 | 문장 또는 들여쓰기 오류 | 콜론·블록·지원 문법 |
14
+ | TS2304 | 찾을 수 없는 이름 | 오타·선언·스코프 |
15
+ | TS2307 | 찾을 수 없는 모듈 | 상대 경로·실제 파일·타입 선언 |
16
+ | TS2322 | 대입 타입 불일치 | 변수 타입과 넣는 값 |
17
+ | TS2339 | 없는 속성·메서드 | 대상 타입과 속성 이름 |
18
+ | TS2345 | 인자 타입 불일치 | 함수에 넘긴 값의 타입 |
19
+ | TS2554 | 인자 개수 불일치 | 필수 인자의 개수 |
20
+ | TS7006 | 명시되지 않은 매개변수 타입 | 숫자·문자열 등 타입 |
21
+
22
+ `any` 또는 `아무거나`는 타입 검사를 느슨하게 만듭니다. 빈 목록 `[]`의 기본 타입도 현재 `any[]`이므로 엄격히 검사하려면 `점수들: 목록<숫자>는 []이다`처럼 적으세요. 타입이 맞아도 0으로 나누기·잘못된 범위·사용자 규칙 위반은 별도의 실행 검사가 필요합니다.
23
+
24
+ 문자열 길이와 진단 열은 JavaScript UTF-16 기준입니다. `목록으로("문자열")`의 문자열 순회는 코드 포인트 기준입니다. 이모지나 결합 문자의 사용자 인식 글자 수와는 다를 수 있습니다.
@@ -0,0 +1,28 @@
1
+ # 모듈과 JavaScript로 배포하기
2
+
3
+ 관련 있는 함수를 `.hgs` 파일에 묶고 공개합니다. 아래 첫 코드는 `math.hgs`입니다.
4
+
5
+ ```hgs
6
+ 더함(가: 숫자, 나: 숫자)를 정의한다:
7
+ 돌려주기 가 + 나
8
+ 더함을 공개한다
9
+ ```
10
+
11
+ 진입 파일 `main.hgs`에서 상대 경로로 가져옵니다.
12
+
13
+ ```hgs
14
+ "./math.hgs"에서 { 더함 }을 가져온다
15
+ 출력(더함(2, 3))
16
+ ```
17
+
18
+ ```bash
19
+ npx ksc run main.hgs
20
+ node --import @uiwwsw/hangulscript/register main.hgs
21
+ npx ksc build main.hgs --out-dir dist
22
+ ```
23
+
24
+ 직접 실행은 소스 옆에 생성물을 남기지 않습니다. 빌드는 진입 폴더 아래 HGS 의존성도 함께 만들며 import 경로를 `.js`로 바꿉니다. `dist/main.js`, `dist/main.d.ts`, `dist/math.js`, `dist/math.d.ts`가 생성됩니다. 진입 폴더 밖 HGS 의존성은 오류로 처리하므로 공통 상위 폴더에 진입 파일을 두세요.
25
+
26
+ JavaScript와 TypeScript에서는 생성된 `.js`를 가져옵니다. TypeScript는 인접한 `.d.ts`를 읽습니다. 출력은 ESM이므로 소비 프로젝트에 `"type": "module"`을 설정합니다. 기존 JS 파일과 자산은 HGS 빌드가 복사하지 않습니다.
27
+
28
+ `ksc --check main.hgs`는 타입만 검사합니다. `ksc --typescript main.hgs`는 내부 변환 TypeScript를 확인하는 옵션이며 별도 소스 문법이 아닙니다. 원본 디버깅 소스맵·자동 빌드 감시 CLI·독립 네이티브 바이너리 출력은 아직 제공하지 않습니다.
@@ -0,0 +1,22 @@
1
+ # 첫 프로그램 만들기
2
+
3
+ 한글스크립트는 `.hgs` 파일에 문장형 코드와 들여쓰기를 작성합니다. 시작 표기는 필요하지 않습니다. Node.js 20.19 이상을 설치하고 아래 순서대로 실행하세요.
4
+
5
+ ```bash
6
+ npm install --save-dev @uiwwsw/hangulscript@^0.8.0
7
+ npx ksc run main.hgs
8
+ ```
9
+
10
+ `main.hgs`에 다음을 저장합니다.
11
+
12
+ ```hgs
13
+ 점수들은 [75, 92, 88]이다
14
+ 각 점수를 점수들에서:
15
+ 만약 점수 >= 80 이라면:
16
+ 출력(점수)
17
+ 출력("평균:", 평균(점수들))
18
+ ```
19
+
20
+ 배포에는 `npx ksc build main.hgs --out-dir dist`를 사용합니다. JavaScript와 타입 선언 `.d.ts`가 함께 생성됩니다. 생성된 `.js`를 Node에서 실행할 프로젝트에는 package.json의 `"type": "module"`을 설정하세요.
21
+
22
+ 설치 없이 확인하려면 [실행 화면](/#playground)을 사용하세요. 공유한 코드는 자동 실행하지 않습니다. 브라우저는 격리된 실행 환경에서 최대 2초 동안 동작합니다.
@@ -0,0 +1,29 @@
1
+ # 뜻이 분명한 문장으로 개발하기
2
+
3
+ 잘 쓴 코드에는 좋은 글과 닮은 점이 있습니다. 대상의 이름이 분명하고, 행동의 이유가 있으며, 앞에서 정한 규칙이 뒤에서도 지켜집니다. 한글스크립트에서는 이를 이름·타입·조건·결과·모듈로 표현합니다.
4
+
5
+ - 인물과 사물: 자료의 이름과 타입을 정합니다.
6
+ - 세계의 규칙: 허용하는 값과 불변 조건을 분명히 씁니다.
7
+ - 사건: 함수가 무엇을 받아 무엇을 돌려주는지 정합니다.
8
+ - 장면: 관련 있는 함수를 모듈로 묶습니다.
9
+ - 앞뒤의 일관성: 타입 검사와 테스트로 확인합니다.
10
+
11
+ ```hgs
12
+ 할인후금액(가격: 숫자, 할인율: 숫자)를 정의한다:
13
+ 확인한다(Number.isFinite(가격) && 가격 >= 0, "가격은 유한한 0 이상의 수입니다")
14
+ 확인한다(Number.isFinite(할인율) && 할인율 >= 0 && 할인율 <= 100, "할인율은 0~100입니다")
15
+ 결과는 가격 - 반올림(가격 * 할인율 / 100)이다
16
+ 확인한다(결과 >= 0, "결제 금액은 음수가 아닙니다")
17
+ 돌려주기 결과
18
+
19
+ 확인한다(할인후금액(10000, 20) === 8000, "20% 할인 계산을 확인합니다")
20
+ 출력(할인후금액(10000, 20))
21
+ ```
22
+
23
+ 이 예제는 입력 조건·처리·결과 조건을 코드에 남깁니다. 타입 검사는 숫자 자리에 문자열을 전달하는 오류를 찾고, `확인한다`는 실행 중 조건 위반을 찾습니다. 모든 경우의 올바름을 수학적으로 증명하는 기능은 아닙니다.
24
+
25
+ ## AI API가 꼭 필요한가요?
26
+
27
+ 필요하지 않습니다. 정해진 문법, 타입, 조건 검사, 명시적인 예외, 실행 가능한 예제만으로도 문장이 명확할수록 검사 가능한 내용을 늘릴 수 있습니다. 현재 언어는 소설이나 임의의 산문을 자동으로 실행하지 않습니다.
28
+
29
+ 자유로운 글에서 프로그램 후보를 제안하는 보조 기능은 향후 AI로 만들 수 있습니다. 로컬 모델도 가능하며 외부 API만이 방법은 아닙니다. 다만 생성된 코드에는 같은 컴파일·타입·테스트 검증이 필요합니다. 핵심 문법과 실행은 AI 서비스 없이 동작합니다.
@@ -0,0 +1,13 @@
1
+ # npm 자동 게시
2
+
3
+ 저장소 Actions Secret **NPM_TOKEN**을 사용합니다. 토큰은 소스·워크플로 본문·.env 파일에 넣지 않습니다. 현재 로그인에 쓰는 개인 세션 토큰을 재사용하지 말고 npm에서 이 패키지에만 권한을 주는 granular access token을 발급하세요.
4
+
5
+ 1. npm Access Tokens에서 만료 기간을 정하고 `@uiwwsw/hangulscript`에만 읽기·쓰기 권한을 부여합니다. 무인 게시에는 토큰의 게시용 2FA 우회 권한과 패키지의 해당 토큰 게시 허용 정책이 모두 맞아야 합니다. 계정 전체 2FA를 끄지 않습니다.
6
+ 2. GitHub 저장소 Settings → Secrets and variables → Actions → New repository secret에서 이름을 `NPM_TOKEN`으로 등록합니다. 토큰 값은 채팅·이슈·문서에 붙이지 않습니다.
7
+ 3. package.json 버전을 높여 커밋하고 동일한 `v버전` 태그의 GitHub Release를 게시합니다. 이미 존재하는 버전은 다시 게시할 수 없습니다.
8
+ 4. `Publish npm package` 작업이 태그·패키지명·시크릿 존재를 검사하고 테스트·빌드 후 생성한 tarball을 provenance와 함께 게시합니다. 수동 실행은 동일한 릴리스 태그를 입력합니다. 시험판은 `next`, 정식 버전은 `latest` 태그를 사용합니다.
9
+ 5. npm 공개 버전과 다운로드 체크섬을 확인합니다. 토큰이 만료되면 전용 토큰을 교체하고 같은 시크릿을 갱신합니다.
10
+
11
+ 워크플로는 PR·브랜치 push로 게시되지 않습니다. npm 게시 권한은 게시 단계에만 전달하며 출력하지 않습니다. 토큰을 등록하지 않은 상태에서는 명확한 오류로 중단합니다.
12
+
13
+ 토큰 보관을 없애려면 npm Trusted Publishing(OIDC)을 별도로 구성할 수도 있습니다. [npm 공식 토큰 안내](https://docs.npmjs.com/creating-and-viewing-access-tokens/) · [Trusted Publishing](https://docs.npmjs.com/trusted-publishers/)
package/docs/support.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 지원 범위와 호환성
2
2
 
3
- 한글스크립트는 명시된 한국어 문법을 JavaScript ES 모듈로 컴파일합니다. 임의의 자연어를 추측하지 않으며 별도 시작 표기는 사용하지 않습니다. 이 문서는 0.7.0 언어 패키지와 0.2.0 VS Code 확장의 지원 계약입니다.
3
+ 한글스크립트는 명시된 한국어 문법을 JavaScript ES 모듈로 컴파일합니다. 임의의 자연어를 추측하지 않으며 별도 시작 표기는 사용하지 않습니다. 이 문서는 0.8.0 언어 패키지와 0.3.0 VS Code 확장의 지원 계약입니다.
4
4
 
5
5
  | 환경 | 지원 내용 | 검증 |
6
6
  | --- | --- | --- |
@@ -13,7 +13,7 @@
13
13
  ## Node에서 사용하기
14
14
 
15
15
  ```bash
16
- npm install @uiwwsw/hangulscript@^0.7.0
16
+ npm install @uiwwsw/hangulscript@^0.8.0
17
17
  npx ksc run src/main.hgs
18
18
  # 또는 Node의 등록 진입점으로 직접 로드
19
19
  node --import @uiwwsw/hangulscript/register src/main.hgs
@@ -40,3 +40,5 @@ Vite가 아닌 환경에서 `.hgs` 파일을 바로 가져오려면 해당 로
40
40
  [VS Code 설치 및 기능 안내](../extensions/vscode/README.md)를 참고하세요. 확장은 컴파일러와 같은 저장소에서 관리하며 독립 버전으로 배포합니다. 편집기 검사는 Worker에서 실행하며 사용자 코드는 실행하지 않습니다. 자동 완성은 스코프의 이름과 스니펫을 제공합니다. 전체 속성 자동 완성, 자동 import, 이름 변경, 디버거, vscode.dev는 아직 지원하지 않습니다.
41
41
 
42
42
  0.x에서는 문법의 호환성을 깨는 변경을 minor 버전에서만 진행하고 변경 내역·마이그레이션을 안내합니다. patch 버전은 호환되는 수정에 사용합니다. 1.0은 별도의 문법 동결과 호환성 검증을 거쳐 결정합니다. 사용자용 확장을 Marketplace에 게시하는 것과 Microsoft가 기본 내장 언어로 채택하는 것은 별개입니다.
43
+
44
+ 0.8.0은 목록·집합·사전·복사·정렬 등을 포함한 61개 한글 함수를 제공합니다. 전체 JavaScript API의 한글 별칭, 데이터베이스·네트워크·GUI 프레임워크 전체를 제공한다는 뜻은 아닙니다. 지원하지 않는 기능은 기존 JS/TS 생태계와 연결하거나 별도 구현해야 합니다. [표준 기능 참조](https://hangulscript.vercel.app/docs/standard-library/)에서 제공 범위를 확인하세요.
@@ -0,0 +1,19 @@
1
+ 점수들은 [88, 92, 75, 88]이다
2
+ 점수순은 점수들을 내림차순으로 정렬한 값이다
3
+ 고유값은 점수들에서 중복을 제거한 값이다
4
+ 통과한값은 골라내기(점수들, 점수 => 점수 >= 80)이다
5
+
6
+ 재고: 사전<문자열, 숫자>는 사전()이다
7
+ 사전에담기(재고, "사과", 3)
8
+ 사전에담기(재고, "배", 5)
9
+
10
+ 원본은 [{ 점수: 10 }]이다
11
+ 얕은복사본은 원본을 얕게 복사한 값이다
12
+ 깊은복사본은 원본을 깊게 복사한 값이다
13
+ 얕은복사본[0].점수 = 20
14
+
15
+ 확인한다(원본[0].점수 === 20, "얕은 복사는 안쪽 참조를 공유합니다")
16
+ 확인한다(깊은복사본[0].점수 === 10, "깊은 복사는 안쪽 값도 복제합니다")
17
+ 출력("정렬:", 점수순, "중복 제거:", 고유값, "통과:", 통과한값)
18
+ 출력("재고:", 항목목록(재고))
19
+ 출력("원본과 깊은 복사본:", 원본[0].점수, 깊은복사본[0].점수)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uiwwsw/hangulscript",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "한글 문장으로 작성하고 JavaScript와 타입 선언으로 배포하는 프로그래밍 언어",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -77,7 +77,8 @@
77
77
  "devDependencies": {
78
78
  "@vscode/test-electron": "^3.1.0",
79
79
  "@vscode/vsce": "^4.0.0",
80
- "esbuild": "^0.25.0"
80
+ "esbuild": "^0.25.0",
81
+ "marked": "^18.1.1"
81
82
  },
82
83
  "types": "dist/index.d.ts"
83
84
  }