Spintax 구문 참조
spintax 템플릿 마크업에 대한 완전한 참조.
열거 { }
목록에서 무작위로 하나의 옵션을 선택합니다.
{option1|option2|option3}
예제
{blue|grey|clear}
{|free|paid} plan ← empty option = sometimes nothing
{Acme {Pro|Lite}} ← nested enumerations
{order {|#42-A} confirmed} ← nesting with empty option
규칙
- 구분자:
{및} - 구분자:
| - 임의의 깊이까지 중첩 지원
- 빈 옵션은 유효 (빈 문자열 생성)
- 가장 안쪽 표현식에서 바깥쪽으로 해석
순열 [ ]
N개의 요소를 선택, 셔플, 구분자로 결합합니다.
단순 순열
모든 요소 포함, 공백으로 구분:
[1|2|3|4]
출력 예: 1 4 3 2, 2 3 4 1, 3 2 4 1
구분자 포함
시작 부분의 < >에 통일 구분자 지정:
[<, > 1|2|3|4]
출력 예: 2, 1, 4, 3 · 4, 3, 2, 1
중요: [과 <구분자> 사이에 공백 없음.
요소별 구분자
각 옵션은 이전 | 앞에 <sep>로 고유한 구분자를 정의할 수 있습니다. 구분자는 셔플 시 해당 요소와 함께 이동합니다.
[<, > 1|2|3 < and >|4]
출력 예: 1, 3, 2 and 4 · 3, 1, 2 and 4
자동 띄어쓰기: <and> 또는 <or>와 같은 단어 구분자는 자동으로 공백이 추가되어 <and>는 and 가 됩니다. 문장부호 구분자(<,>)는 추가되지 않습니다.
조합이 있는 순열
구성 가능한 최소/최대 요소 수와 구분자:
[<minsize=1;maxsize=3;sep=", ";lastsep=" and "> apple|plum|orange|apricot]
출력 예: apple, plum and orange · apple and apricot · orange
구성 매개변수
| 매개변수 | 기본값 | 설명 |
|---|---|---|
minsize | 전체 수 | 선택할 최소 요소 수 |
maxsize | 전체 수 | 선택할 최대 요소 수 |
sep | " " (공백) | 마지막이 아닌 항목 간 구분자 |
lastsep | sep과 동일 | 마지막 요소 앞 구분자 |
순열 규칙
- 구분자:
[및] - 구성 블록
<...>은[바로 뒤에 와야 합니다 - 구성 매개변수는 세미콜론으로 구분
- 구성의 문자열 값은 따옴표로 감쌈:
sep=", " - 열거와 순열은 옵션 내에서 중첩 가능
- HTML 요소도 옵션이 될 수 있음
sep은 마지막 쌍 앞의 모든 항목을,lastsep은 그 쌍을 잇습니다. 두 항목이 선택되면lastsep만 나타나고, 하나면 둘 다 나타나지 않습니다
변수 %var%
나타나는 모든 자리에서 치환되는 재사용 가능한 변수를 정의합니다. 선언하는 지시자는 두 가지이고, 그 선택은 겉모습의 차이가 아닙니다. #set은 매크로여서 참조할 때마다 값이 다시 치환되고 그 안의 spintax도 다시 뽑힙니다. #def는 렌더링당 한 번만 값을 뽑아 그 하나의 결과를 모든 참조에 넘깁니다(렌더링 한 번은 출력 하나이며, 같은 seed가 그것을 재현합니다). 값이 단순한 문자열인 동안에는 둘이 동일하고, 차이는 값에 선택이 들어오는 순간 드러납니다.
#set %VARIABLE_NAME% = value or spintax structure
#def %VARIABLE_NAME% = value or spintax structure
예제
#set %name% = John
#set %greeting% = {Hello|Hi|Hey}
#set %items% = [<minsize=2;maxsize=3;sep=", ";lastsep=" and "> apples|oranges|bananas]
Some text with %name% and %greeting%, also %items%.
And once more, %greeting% — a second reference.
/# %greeting% above may differ between the two references — #set re-rolls.
#def picks once and keeps it: #/
#def %tone% = {friendly|warm|upbeat}
A %tone% intro, and a %tone% outro — always the same word.
변수 규칙
#set과#def는 줄 맨 앞에서 시작해야 합니다- 변수 이름은
%로 감쌉니다:%name% - 이름은 영숫자와 밑줄로 이루어지며 참조는 대소문자를 구분하지 않습니다:
%Tone%과%tone%은 같은 변수입니다 - 값에는 어떤 spintax 문법도 넣을 수 있습니다(열거, 순열, 다른 변수)
#set은 매크로입니다. 정의할 때가 아니라 참조할 때 펼쳐지고, 그 값은 — 안의 spintax까지 포함해 — 참조할 때마다 다시 치환되고 다시 뽑힙니다#def는 렌더링당 한 번만 값을 확정하고 모든 곳에서 그 결과를 유지합니다. 반복되는 단어가 서로 맞는 것은 이 덕분입니다. 명사와 거기서 만든 형태들,{plural}에 넘기는 수, 글자 그대로 일치해야 하는 문구#def는 한 변수를 자기 자신과 일치시킬 뿐, 두 변수를 연동하지 않습니다.#def %Noun%과#def %NounGen%은 독립된 두 번의 추첨이라 서로 다른 단어가 될 수 있습니다 — 반드시 일치해야 하는 형태는 한 번의 추첨에서 나와야 합니다. 모든 형태가 참조하는#def어간을 하나 두거나, 같은 방식으로 변화하는 동의어를 골라 어미를 정의 밖에 씁니다- 이름은 한 번만 정의합니다. 같은 이름의 두 번째 정의는
definition.duplicate-name으로 보고되지만 렌더링은 그대로 진행됩니다. 같은 지시자끼리는 나중 것이 이기고,#set과#def가 이름을 공유하면 어느 쪽이 먼저든#def가 이깁니다 - 정의가 없는 참조는 자기 자신을 출력합니다:
%missing%은 사라지지 않고 출력에 남습니다 - 두 지시자 모두
#include를 넘지 않습니다. 포함된 템플릿은 부모의 지역 변수를 보지 못하고, 자신의 것도 위로 새지 않습니다. 이미 뽑힌 형태는 런타임 변수로만 자식에게 전달됩니다 #set과#def줄은 출력에서 제거됩니다- 심화: 변수 가이드(스코프와 재추첨 함정)와 문법적으로 안전한 동의어 치환(격 계열)
변수 스코프
호스트는 여러 곳에서 변수를 공급할 수 있습니다. 같은 이름이 여러 곳에 있으면 가장 강한 쪽이 이깁니다.
- 런타임 변수(가장 강함) — 호스트가 렌더링 호출에 넘기는 값.
@spintax/core의context, WordPress 플러그인의 숏코드 속성:[spintax slug="greeting" name="Alice"] - 지역 변수 — 템플릿 안에서
#set또는#def로 정의 - 전역 변수(가장 약함) — 플러그인 설정 페이지처럼 호스트 전체의 기본값
조건문 {?VAR?then|else}
조건문은 이 언어 고유의 구문입니다. 원형인 GTW 에는 비슷한 것이 없었습니다. {a|b} 는 변수를 보지 않는 균등 랜덤 선택이지만, {?VAR?then|else} 는 %VAR% 에 값이 있는지에 따라 선택합니다.
값 기반 선택에 사용하세요: 무료 등급이 있을 때만 무료 등급 라인을 표시, 사용자가 유료 플랜일 때만 프로 기능 블록을 렌더링, 적용되지 않는 CTA 를 숨기기.
프리패스는 %var% 확장과 랜덤 분기 선택기 이전에 실행되므로 falsy 분기는 완전히 폐기됩니다 — 내부의 어떤 것도 평가되지 않습니다.
형식
{?VAR?then} ← truthy ⇒ then; falsy ⇒ empty
{?VAR?then|else} ← truthy ⇒ then; falsy ⇒ else
{?!VAR?then|else} ← inverted
{?HasFreeTier? — free tier available since %founded%|, trusted since %founded%}
Truthy 와 falsy
규칙은 JavaScript 보다 의도적으로 단순합니다 — truthy = 최소 하나의 비공백 문자:
%VAR% 의 값 | Truthy? |
|---|---|
| 선언되지 않음 | falsy |
| 빈 문자열 | falsy |
| 공백만 | falsy |
"0", "false" | truthy (비어있지 않음) |
| 기타 텍스트 또는 HTML | truthy |
조건문 규칙
- 변수 이름은
%var%와 같은 regex 를 따름 (대소문자 구분 없음) !접두사가 검사를 반전:{?!VAR?없음}- 깊이 0 의 첫 번째
|가then과else를 구분; 이후는else안에서 리터럴 - 중첩 조건문은 바깥부터 평가 — falsy 분기는 short-circuit
- 복합 논리(
&&,||, 비교)는 미지원 — 어셈블러에서 가드 변수를 미리 계산 - 잘못된 형식(
{??yes},{?VAR})은 절대 throw 하지 않음 — 플레이그라운드가 경고로 표시 - 심화: 조건문 spintax 가이드 에 예제와 안티패턴
복수형 {plural %n%: language|languages}
숫자에 맞는 문법적으로 올바른 어형을 고릅니다. 카운트는 콜론 앞에, 어형은 콜론 뒤에 |로 구분해 씁니다.
어형을 결정하는 것은 템플릿이 아니라 렌더 로케일입니다. 따라서 몇 개의 어형을 제공해야 하는지는 그 로케일에 달려 있습니다. 영어는 두 개, 러시아어는 세 개가 필요합니다.
{plural %n%: form1|form2} ← 2-form locale (en, de, es…)
{plural %n%: form1|form2|form3} ← 3-form locale (ru, uk, sr…)
#def %LangCount% = 5
supports %LangCount% {plural %LangCount%: language|languages}
← supports 5 languages
로케일별 어형 수
로케일은 언어 서브태그로 매칭되므로 ru-RU와 ru는 동일하게 동작합니다:
| 로케일 | 어형 수 | 선택 기준 |
|---|---|---|
ru, uk, be, sr, hr, bs | 3 | 1 · 2–4 · 5 이상 |
en을 포함한 그 밖의 모든 로케일 | 2 | 정확히 1 · 나머지 전부 |
어형 개수가 맞지 않으면 엔진이 plural.arity를 보고하고 전각 중괄호로 블록을 그대로 보이게 남깁니다. 잘못된 복수형이 조용히 배포되는 일은 없습니다.
복수형 규칙
- 여는 부분은 공백까지 포함해 리터럴입니다:
{plural.{plural: x}와{pluralN: x}는 복수형 블록이 아닙니다 - 콜론은 필수이며 카운트와 어형을 구분합니다
- 카운트는
%Var%참조 또는 정수 리터럴입니다. 카운트 안의 변수는 어형 선택 전에 치환됩니다 - 음수는 절댓값으로 처리하고
0은 "나머지 전부" 어형을 받습니다 - 카운트 변수는
#set이 아니라#def여야 합니다 —#set은 매크로라서{1|4|9}같은 값은 복수형을 결정하는 시점에 아직 해석되지 않은 spintax이고, 블록 전체가 비어서 렌더링됩니다. 플레이그라운드는 이를plural.count-macro로 표시합니다 - 숫자가 아니거나 정의되지 않은 카운트는 추측하지 않고 블록을 지웁니다
- 심화: 복수형 spintax 가이드에 러시아어 3어형 규칙과 실전 예제
인클루드 #include
지시자 위치에 다른 템플릿을 끼워 넣습니다. #include는 엔진이 혼자 처리할 수 없는 유일한 구성입니다. 엔진은 템플릿 저장소를 갖지 않으므로, 참조를 템플릿 텍스트로 바꾸는 리졸버는 호스트가 제공합니다. 리졸버가 설치되지 않은 곳 — 이 사이트의 플레이그라운드와 MCP 서버, 둘 다 의도적입니다 — 에서는 지시자가 작동하지 않고 리터럴 텍스트로 출력에 남습니다.
#include "hero-text"
/# wrong: text before the directive on the same line leaves it literal #/
Intro: #include "hero-text"
인클루드 규칙
- 지시자는 한 줄 전체를 차지해야 합니다. 앞쪽 들여쓰기는 괜찮지만 참조 뒤에 글자가 오면 안 됩니다 —
텍스트 #include "hero"는 그대로 남습니다 - 참조는 큰따옴표로 감쌉니다. 작은따옴표이거나 따옴표가 없으면 지시자가 아닙니다
- 해석은 호스트의 몫입니다. WordPress 플러그인은 슬러그나 숫자 ID로 찾고, JavaScript 호스트는
includeResolver를 넘깁니다 - 리졸버가 없으면 그 줄은 리터럴로 출력에 남고, 리졸버에 해당 템플릿이 없으면 오히려 줄이 삭제됩니다 — 알 수 없는 대상은 조용히 블록 하나를 앗아갑니다
- 포함된 템플릿은 자체 변수와 spintax를 가질 수 있고, 자체
#include도 가질 수 있습니다 - 포함은 부모의 열거와 순열이 뽑힌 뒤에 해석됩니다. 선택된 가지 안의 포함은 삽입되고, 버려진 가지 안의 것은 아예 일어나지 않습니다
- 연쇄는 동작합니다(템플릿이 다른 템플릿을, 그것이 또 다른 것을 포함). 직접이든 순환이든 자기 자신을 포함하는 템플릿은 첫 반복에서 끊깁니다 — 오류도 진단도 없습니다
- 자식 템플릿은 전역 변수와 런타임 변수를 물려받지만 부모의
#set/#def지역 변수는 받지 않고, 자신의 것도 위로 새지 않습니다 - 포함은 정의의 값이 될 수 없습니다:
#def %x% = #include "y"는def.include-in-value로 거부됩니다 - 렌더링 전에
validate()는 호스트가 알려진 참조 목록을 넘길 때만 알 수 없는 대상을 지적합니다.extract()는 템플릿에 필요한 참조를 돌려주며, 호스트는 이것으로 미리 불러옵니다 - 심화: 템플릿 컴포지션 가이드 — 대부분의 파이프라인이 대신 쓰는 어셈블러 패턴
주석 /#...#/
주석 마커 사이의 텍스트는 다른 처리 전에 출력에서 제거됩니다.
/#
This is a comment section.
It can span multiple lines.
It won't appear in output.
#/
주석 규칙
- 시작 구분자:
/# - 종료 구분자:
#/ - 여러 줄에 걸칠 수 있음
- 중첩 불가
- 다른 처리 전에 제거
중첩
모든 구문 요소는 임의의 깊이로 서로 중첩할 수 있습니다:
{option1|[<, > sub1|sub2|sub3]|option3}
[<minsize=2;maxsize=3;sep=", ";lastsep=" and "> {red|blue} apples|{big|small} oranges|bananas]
#set %var% = {a|[b|c]}
후처리
엔진은 생성 후 자동 텍스트 교정을 적용합니다:
- URL, 이메일, 도메인, 소수, 약어를 대문자화로부터 보호
- 중복 공백 및 탭 제거
- 구두점 앞의 공백 제거 (
,.!?) - 구두점 뒤에 누락된 공백 추가
- 출력의 첫 글자를 대문자로 (HTML 태그 건너뜀)
- 문장 끝 구두점 뒤 대문자화
- 블록 수준 HTML 태그 뒤 대문자화
- 줄바꿈 뒤 대문자화
- 보호된 플레이스홀더 복원
구문 요약
| 기능 | 구문 | 동작 |
|---|---|---|
| 열거 | {a|b|c} | 무작위 옵션 선택 |
| 순열 | [a|b|c] | N개 선택, 셔플, 결합 |
| 구분자 | [<sep> a|b|c] | 통일 구분자 순열 |
| 요소별 구분자 | [<, > a|b <x>|c] | 사용자 정의 구분자 순열 |
| 조합 | [<config> a|b|c] | 최소/최대 수 순열 |
| 변수 | #set %var% = {a|b} | 참조할 때마다 다시 치환되고 안의 spintax도 다시 뽑힘 |
| 변수 (한 번만) | #def %var% = {a|b} | 렌더링당 한 번만 뽑아 모든 참조에서 유지 — 어형과 어미가 이렇게 일치 |
| 조건문 | {?VAR?then|else} | truthy면 then, falsy면 else |
| 복수형 | {plural %n%: language|languages} | 로케일에 따라 어형을 숫자에 일치시킴 |
| 인클루드 | #include "slug" | 다른 템플릿 삽입 — 참조는 호스트가 해석 |
| 주석 | /#...#/ | 출력에서 제거 |
이 언어는 원형인 Generating The Web (GTW)을 넘어 성장했습니다. GTW용으로 작성된 템플릿은 지금도 그대로 동작합니다.