문법
내장 스크립트 언어는 깔끔하고 제한된 Python 문법 부분집합을 따른다. 표준 Python에서 의도적으로 제외되었거나 변경된 기능은 Python과의 차이점에 요약되어 있다.
줄 구조와 들여쓰기
- 코드 블록은 들여쓰기로 구분한다. 들여쓰기는 반드시 공백 4칸의 배수만 사용하며 탭 문자는 허용하지 않는다.
- 주석은
#으로 시작하여 그 줄 끝까지 이어진다. 빈 줄과 주석만 있는 줄은 무시된다. - 각 문장은 한 줄에 하나씩 작성한다. 백슬래시 줄 연결(
\)과 세미콜론(;)은 지원하지 않으나, 소괄호(...)또는 대괄호[...]안의 식은 여러 줄에 걸쳐 작성할 수 있다.
식별자와 예약어
- 식별자는
[A-Za-z_][A-Za-z0-9_]*형식을 따른다. 밑줄 두 개(__)로 시작하는 이름은 예약되어 있어 사용할 수 없다. - 예약어 목록:
def,return,if,elif,else,for,in,break,continue,pass,and,or,not,True,False. - 사용자 정의 이름은 내장 함수 이름을 가릴 수 없으며, 이미 사용된 이름의 종류(함수, 변수, 상수, 포트)를 다른 종류로 바꿀 수 없다.
- 최상위에서 영문 대문자, 숫자, 밑줄로만 이루어진 식별자(예:
BLOCK_SIZE = 16)에 값을 대입하면 불변 상수로 정의된다.
리터럴
| 타입 | 문법 |
|---|---|
int | 10진수 255, 16진수 0xFF, 2진수 0b1010. 밑줄 구분자(1_000)를 사용할 수 있으며 음수는 단항 - 연산자로 표현한다. |
bool | True, False |
bytes (16진수) | h"87 23 af" |
bytes (2진 비트열) | b"1010 0001" |
bytes (UTF-8 텍스트) | s"label" (UTF-8로 인코딩된 바이트열) |
string | "...". 지원 이스케이프: \\, \", \n, \r, \t, \uNNNN. 작은따옴표 문자열은 사용하지 않는다. |
string (서식 문자열) | f"len={n}" (문자열 포맷팅 참고). |
list | [e1, e2, ...]. 모든 원소의 타입이 같아야 한다. 빈 리스트는 xs: list[bytes] = []처럼 명시적 타입 표기가 필수이다. |
바이트 리터럴
- 접두어(
h,b,s)는 여는 큰따옴표에 공백 없이 바로 붙여야 한다(h "87"은 문법 에러). h"...": 16진수 숫자와 공백만 포함한다(공백은 무시됨).0x접두어는 쓸 수 없으며 전체 16진수 자리수는 짝수여야 한다.h""는 빈 바이트열이다.b"...":0,1, 공백만 포함한다. 8비트가 1바이트를 이루므로 전체 비트 개수는 반드시 8의 배수여야 한다.s"...": 문자열의 문자들을 UTF-8 바이트열로 인코딩한다.
Python과 달리
b"..."는 ASCII 바이트 리터럴이 아니라 2진 비트열 리터럴(0과 1)이다. 16진수 바이트에는 h"..."를, ASCII·UTF-8 텍스트 바이트에는 s"..."를 사용한다.| 식 | 결과 (16진수) |
|---|---|
h"87 23 af" | 8723af |
b"1000 0111" | 87 |
s"0123" | 30313233 |
s"0123" == h"30313233" | True |
소괄호 안에서 인접한 같은 종류의 리터럴은 줄을 넘어 자동으로 이어 붙여지므로, 줄마다 주석을 달아 여러 줄의 키나 테스트 벡터를 깔끔하게 작성할 수 있다:
KEY = (h"00 11 22 33 44 55 66 77" # first 8 bytes
h"88 99 aa bb cc dd ee ff") # last 8 bytes
o = KEY
문장(Statements)
| 문장 종류 | 형태 |
|---|---|
| 대입문 | x = expr |
| 타입 명시 대입문 | x: int = expr |
| 복합 대입문 | x += e (그 외 -= *= //= %= &= |= ^= <<= >>=). x = x op e와 동일하다. |
| 첨자(Subscript) 대입 | b[i] = v, b[a:c] = part, b[o, n] = part, xs[i] = v (인덱싱과 슬라이싱 참고). |
| 튜플 언패킹 | a, b = f(x), a, b = b, a |
| 조건문 | if / elif / else |
| 반복문 | for x in iterable: |
| 흐름 제어 | return, break, continue, pass |
| 호출문 | 단독 함수 호출 (예: fail("CODE", "message")). |
변수의 정적 타입은 첫 대입 시점에 고정된다. 조건식은 val1 if cond else val2 형태이며 두 분기의 타입이 같아야 한다:
o = "big" if n > 255 else "small"
함수와 상수
- 함수는 최상위에서만 정의할 수 있으며 중첩 함수는 허용하지 않는다.
- 모든 매개변수에는 타입 표기가 필수이다. 반환 타입은
-> T또는-> tuple[T1, T2]로 표기한다. 반환값이 없는 프로시저는->를 생략하며 단독 호출문으로만 호출한다. - 사용자 정의 함수는 매개변수 기본값, 키워드 인자, 가변 인자를 지원하지 않는다.
- 함수 본문 안에서는 자신의 매개변수, 지역 변수, 파일에서 앞서 정의된 함수, 앞서 정의된 상수, 내장 함수만 참조할 수 있다. 입력·출력 포트 변수에는 직접 접근할 수 없으며, 이 규칙에 의해 재귀 호출 및 상호 재귀도 원천 차단된다.
- 값을 반환하는 함수의 모든 실행 경로는 반드시
return문(또는fail)으로 끝나야 한다. - 상수(Constants)는 최상위에서 대문자 식별자(
[A-Z0-9_]+)로 정의한 대입문이다. 초기화 식에는 리터럴, 앞서 정의된 상수, 내장 함수만 사용할 수 있다. 상수는 불변이며 함수 내부에서도 읽을 수 있다.
HEADER_LEN = 1
def split_head(x: bytes) -> tuple[bytes, bytes]:
return x[:HEADER_LEN], x[HEADER_LEN:]
first, rest = split_head(b)
size = len(b)
인덱싱과 슬라이싱 (Subscripts)
| 형태 | 대상 | 결과 |
|---|---|---|
b[i] | bytes | 0..255 범위의 단일 바이트 int (음수 인덱스는 뒤에서부터 계산). |
b[a:c], b[:c], b[a:], b[:] | bytes | 인덱스 a부터 c 직전까지의 부분 bytes. |
b[o, n], b[o,] | bytes | 오프셋과 길이 기준 부분 bytes (n을 생략하면 끝까지). |
xs[i], xs[a:c], xs[o, n] | list[T] | 원소 T 또는 부분 리스트 list[T]. |
Python과 달리 범위를 벗어난 인덱스나 슬라이스는 조용히 잘리지 않고, 0 ≤ start ≤ end ≤ len을 만족하지 않으면 즉시 BYTES_OUT_OF_RANGE(리스트는 SCRIPT_INDEX_OUT_OF_RANGE) 에러가 발생한다. b[o, n]에서 음수 n은 뒤에서부터 센 끝 위치로 동작한다(b[o:n]과 동일).
아래 표에서 a는 00 11 22 33 44 55 66 77 88이다:
| 식 | 결과 (16진수) |
|---|---|
a[1, 3] | 112233 (오프셋 1부터 3바이트) |
a[6,] | 667788 (오프셋 6부터 끝까지) |
a[6, -1] | 6677 (a[6:-1]과 동일) |
a[6, -2] | 66 |
a[-3, 3] | 667788 (음수 오프셋) |
a[1:4] | 112233 |
a[6:] | 667788 |
a[6:-1] | 6677 |
a[-1] | 136 (0x88) |
첨자 대입은 바이트 또는 리스트 원소를 제자리에서 갱신한다. 슬라이스 대입 시 우변 bytes의 길이는 대상 슬라이스 길이와 정확히 같아야 한다(다르면 BYTES_LENGTH_MISMATCH 에러 발생):
o = a
o[1, 2] = h"ff ee"
for 반복문과 range
for x in xs:는list[T]의 원소를 순회한다.for v in b:는bytes의 각 바이트를0..255범위의int로 순회한다.for i in range(n),range(start, stop),range(start, stop, step)은 정수를 순회한다.range는 오직for문의 순회 대상으로만 사용할 수 있으며step은0일 수 없다.- 단일 반복문은 최대 1,048,576회까지 반복할 수 있다(루프 진입 시 검사).
total = 0
for v in b:
if v == 2:
continue
if v == 5:
break
total += v
문자열 포맷팅 (f-strings)
f"..."는 식의 값을 string 안에 삽입한다. 포맷 지정자는 [[fill]align][sign][#][0][width][type] 형식을 따른다:
| 값 타입 | 허용 포맷 타입 |
|---|---|
int | d (기본값), x, X, b, o. 부호(+, -, 공백), #, 0 채움(0)은 int에만 적용된다. |
string | s (폭, 채움 문자, 정렬). |
bytes | x (기본값), X (바이트당 2자리 16진수 연속 출력). |
bool | s (True / False). |
| 식 | 결과 |
|---|---|
f"{n:03d}" | 007 |
f"{v:02x}" | 0a |
f"{v:08b}" | 00000101 |
f"{v:#x}" | 0xff |
f"{v:#x}" | -0xff |
f"{v:+d}" | +3 |
f"{name:>8}|{key:X}" | ab|0AFF |
f"{{{n}}}" | {7} |
Python과의 차이점
| 기능 | Python | NodeCrypto 내장 스크립트 |
|---|---|---|
import, class, lambda, 컴프리헨션, try, while, with, global, print | 지원 | 설계상 제외 |
속성 접근(.) 및 메서드 | 지원 | 사용하지 않음; 내장 함수(len(x), to_hex(b))를 직접 호출 |
/, **, 부동소수점 수 | 지원 | 제외 (정수 나눗셈 // 및 모듈러 거듭제곱 pow(b, e, m) 사용) |
| 암묵적 참·거짓 변환(Truthiness) | 모든 타입에 허용 | 조건식은 반드시 bool 타입이어야 함 (if x & 0x80 != 0:처럼 작성) |
| 바이트 리터럴 | b"\x87\x23" | h"87 23" (16진수), b"1000 0111" (비트열), s"text" (UTF-8) |
| 슬라이스 표기 | b[1:4]만 지원 | b[1:4]와 오프셋+길이 표기 b[1, 3] 모두 지원 |
| 범위를 벗어난 슬라이스 | 조용히 잘라냄 | BYTES_OUT_OF_RANGE 에러 발생 |
bytes 비트 연산 (& | ^ ~ << >>) | 정의되지 않음 | 바이트 배열 단위 비트 연산 및 시프트 기본 제공 |
| 대입 의미론 | 리스트 등 참조 공유 | 순수 값 복사(Value Copy) 의미론 |