f8b21af4be
10_Wiki/Topics 대규모 정리: - 오류 캡처/미완성 stub 문서 227개 제거 - 교차폴더 중복 43클러스터 병합 (63파일 → redirect) - 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건 - 카테고리 MOC 6개 신규 생성 - Graph 섹션 미해결 related-keyword 링크 10,058건 제거 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
352 lines
8.9 KiB
Markdown
352 lines
8.9 KiB
Markdown
---
|
|
id: wiki-2026-0508-dsl
|
|
title: Domain-Specific Languages (DSL)
|
|
category: 10_Wiki/Topics
|
|
status: verified
|
|
canonical_id: self
|
|
aliases: [DSL, internal DSL, external DSL, fluent API, embedded DSL, NL-to-DSL]
|
|
duplicate_of: none
|
|
source_trust_level: A
|
|
confidence_score: 0.9
|
|
verification_status: applied
|
|
tags: [dsl, programming-languages, abstraction, fluent-api, ddd, sql, regex, llm-dsl]
|
|
raw_sources: []
|
|
last_reinforced: 2026-05-10
|
|
github_commit: pending
|
|
tech_stack:
|
|
language: any
|
|
framework: ANTLR / PEG / Tree-sitter
|
|
---
|
|
|
|
# DSL (Domain-Specific Language)
|
|
|
|
## 매 한 줄
|
|
> **"매 specific domain 의 optimized 의 language"**. 매 SQL, regex, HTML, CSS 의 omnipresent. 매 internal (embedded) vs external. 매 modern: 매 LLM 의 NL → DSL 의 best interface. 매 IaC 의 Terraform / config DSL 의 boom.
|
|
|
|
## 매 핵심
|
|
|
|
### 매 type
|
|
|
|
#### External DSL
|
|
- 매 own grammar.
|
|
- 매 parser / compiler 필요.
|
|
- 예: SQL, regex, CSS, GraphQL, Gherkin, HCL (Terraform).
|
|
|
|
#### Internal DSL (Embedded)
|
|
- 매 host language 의 문법 안.
|
|
- 매 fluent API.
|
|
- 예: jQuery, RSpec, Gradle (Kotlin DSL), zod schema.
|
|
|
|
### 매 design principle
|
|
1. **Domain-aligned terminology**.
|
|
2. **Declarative over imperative** ("what" not "how").
|
|
3. **Composable**.
|
|
4. **Readable by domain expert**.
|
|
5. **Constrained** (less Turing-complete = better verification).
|
|
|
|
### 매 famous DSL
|
|
- **SQL**: 매 query.
|
|
- **Regex**: 매 pattern.
|
|
- **CSS**: 매 styling.
|
|
- **HTML / JSX**: 매 markup.
|
|
- **GraphQL**: 매 query.
|
|
- **Cypher** (Neo4j): 매 graph.
|
|
- **HCL** (Terraform): 매 IaC.
|
|
- **Gherkin**: 매 BDD.
|
|
- **Mermaid / PlantUML**: 매 diagram.
|
|
- **dbt SQL**: 매 transformation.
|
|
|
|
### 매 modern (LLM era)
|
|
|
|
#### NL-to-DSL
|
|
- 매 natural language → 매 SQL / regex / etc.
|
|
- 매 GitHub Copilot regex helper.
|
|
- 매 Text2SQL 의 mature.
|
|
|
|
#### LLM-driven DSL
|
|
- 매 LLM 의 better understand 매 specific DSL.
|
|
- 매 internal company DSL 의 valuable.
|
|
|
|
### 매 tooling
|
|
- **ANTLR**: 매 parser generator.
|
|
- **PEG.js / Peggy**: 매 PEG-based.
|
|
- **Tree-sitter**: 매 modern incremental.
|
|
- **Lark** (Python).
|
|
- **MPS** (JetBrains): 매 projectional.
|
|
|
|
### 매 응용
|
|
1. **Configuration** (Helm, Terraform).
|
|
2. **Workflow** (Airflow, GitHub Actions YAML).
|
|
3. **Test specification** (Gherkin, RSpec).
|
|
4. **Build** (Gradle, Bazel).
|
|
5. **Domain modeling** (DDD ubiquitous language).
|
|
6. **Hardware** (Verilog, VHDL).
|
|
7. **Math / AI** (TLA+, Lean).
|
|
|
|
## 💻 패턴
|
|
|
|
### Internal DSL (TypeScript fluent)
|
|
```ts
|
|
// 매 query builder
|
|
class QueryBuilder<T> {
|
|
private filters: Filter[] = [];
|
|
private sort: Sort[] = [];
|
|
private limitN = 100;
|
|
|
|
where(field: keyof T, op: Op, value: unknown): this {
|
|
this.filters.push({ field, op, value });
|
|
return this; // 매 chain
|
|
}
|
|
|
|
orderBy(field: keyof T, dir: 'asc' | 'desc'): this {
|
|
this.sort.push({ field, dir });
|
|
return this;
|
|
}
|
|
|
|
limit(n: number): this {
|
|
this.limitN = n;
|
|
return this;
|
|
}
|
|
|
|
build(): SQL {
|
|
return `SELECT * FROM ${this.table} WHERE ${this.filters} ORDER BY ${this.sort} LIMIT ${this.limitN}`;
|
|
}
|
|
}
|
|
|
|
// 매 use
|
|
const query = new QueryBuilder<User>()
|
|
.where('age', '>', 18)
|
|
.where('country', '=', 'US')
|
|
.orderBy('createdAt', 'desc')
|
|
.limit(50);
|
|
```
|
|
|
|
### Zod schema (DSL-like)
|
|
```ts
|
|
import { z } from 'zod';
|
|
|
|
const UserSchema = z.object({
|
|
id: z.string().uuid(),
|
|
email: z.string().email(),
|
|
age: z.number().int().min(0).max(150),
|
|
role: z.enum(['admin', 'user']),
|
|
preferences: z.object({
|
|
theme: z.enum(['light', 'dark']).default('light'),
|
|
notifications: z.boolean().default(true),
|
|
}).optional(),
|
|
});
|
|
|
|
type User = z.infer<typeof UserSchema>;
|
|
```
|
|
|
|
### External DSL (Lark in Python)
|
|
```python
|
|
from lark import Lark, Transformer
|
|
|
|
grammar = """
|
|
start: rule+
|
|
rule: NAME ":" condition "→" action
|
|
condition: "if" CNAME comparison NUMBER
|
|
comparison: ">" | "<" | "=="
|
|
action: CNAME "(" CNAME ")"
|
|
NUMBER: /\\d+/
|
|
%import common.CNAME
|
|
%import common.NAME
|
|
%import common.WS
|
|
%ignore WS
|
|
"""
|
|
|
|
dsl = """
|
|
high_score: if score > 90 → notify(student)
|
|
warn: if attendance < 70 → email(student)
|
|
"""
|
|
|
|
parser = Lark(grammar)
|
|
tree = parser.parse(dsl)
|
|
|
|
class MyDSL(Transformer):
|
|
def rule(self, items):
|
|
name, cond, action = items
|
|
return {'name': name, 'condition': cond, 'action': action}
|
|
|
|
print(MyDSL().transform(tree))
|
|
```
|
|
|
|
### NL → SQL (LLM-driven)
|
|
```python
|
|
def nl_to_sql(query, schema):
|
|
prompt = f"""You are a SQL expert.
|
|
|
|
Schema:
|
|
{schema}
|
|
|
|
Convert this natural language to SQL:
|
|
"{query}"
|
|
|
|
Return ONLY the SQL, no explanation."""
|
|
sql = llm.generate(prompt)
|
|
|
|
# 매 validate
|
|
if not is_safe(sql) or has_destructive(sql):
|
|
raise ValueError('Unsafe SQL')
|
|
|
|
return sql
|
|
```
|
|
|
|
### Gherkin (BDD)
|
|
```gherkin
|
|
Feature: User login
|
|
|
|
Scenario: Successful login with valid credentials
|
|
Given a registered user with email "test@example.com"
|
|
And the user's password is "password123"
|
|
When the user submits the login form
|
|
Then they are redirected to "/dashboard"
|
|
And a session cookie is set
|
|
```
|
|
|
|
```python
|
|
from behave import given, when, then
|
|
|
|
@given('a registered user with email "{email}"')
|
|
def step_user(context, email):
|
|
context.user = create_user(email)
|
|
|
|
@when('the user submits the login form')
|
|
def step_submit(context):
|
|
context.response = client.post('/login', {...})
|
|
|
|
@then('they are redirected to "{path}"')
|
|
def step_redirect(context, path):
|
|
assert context.response.url.endswith(path)
|
|
```
|
|
|
|
### Terraform HCL
|
|
```hcl
|
|
resource "aws_instance" "web" {
|
|
ami = "ami-0c55b159cbfafe1f0"
|
|
instance_type = var.environment == "prod" ? "t3.large" : "t3.micro"
|
|
|
|
tags = merge(var.common_tags, {
|
|
Name = "web-${var.environment}"
|
|
})
|
|
|
|
lifecycle {
|
|
create_before_destroy = true
|
|
prevent_destroy = var.environment == "prod"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Custom config DSL (Python)
|
|
```python
|
|
class PipelineDSL:
|
|
def __init__(self):
|
|
self.steps = []
|
|
|
|
def fetch(self, source: str):
|
|
self.steps.append(('fetch', source))
|
|
return self
|
|
|
|
def transform(self, fn):
|
|
self.steps.append(('transform', fn))
|
|
return self
|
|
|
|
def store(self, dest: str):
|
|
self.steps.append(('store', dest))
|
|
return self
|
|
|
|
def run(self):
|
|
# 매 execute
|
|
...
|
|
|
|
# 매 use
|
|
pipeline = (PipelineDSL()
|
|
.fetch('s3://my-bucket/data.csv')
|
|
.transform(lambda df: df.dropna())
|
|
.store('postgres://localhost/clean'))
|
|
```
|
|
|
|
### Tree-sitter (parse)
|
|
```js
|
|
const Parser = require('tree-sitter');
|
|
const SQL = require('tree-sitter-sql');
|
|
|
|
const parser = new Parser();
|
|
parser.setLanguage(SQL);
|
|
|
|
const tree = parser.parse('SELECT * FROM users WHERE id = 1');
|
|
console.log(tree.rootNode.toString());
|
|
```
|
|
|
|
### Validate DSL (LLM-friendly)
|
|
```python
|
|
def validate_dsl(text, grammar):
|
|
"""매 LLM 의 generated DSL 의 validate."""
|
|
try:
|
|
parser.parse(text)
|
|
return {'valid': True}
|
|
except ParseError as e:
|
|
# 매 LLM 의 fix 의 ask
|
|
fixed = llm.generate(f'Fix this DSL parse error:\n{e}\n{text}')
|
|
return {'valid': False, 'fixed_attempt': fixed}
|
|
```
|
|
|
|
### DSL 의 evaluation
|
|
```python
|
|
class CompiledDSL:
|
|
def __init__(self, ast):
|
|
self.ast = ast
|
|
|
|
def execute(self, context):
|
|
return self._eval(self.ast, context)
|
|
|
|
def _eval(self, node, ctx):
|
|
if node.type == 'literal': return node.value
|
|
if node.type == 'variable': return ctx[node.name]
|
|
if node.type == 'binary_op':
|
|
left = self._eval(node.left, ctx)
|
|
right = self._eval(node.right, ctx)
|
|
return apply_op(node.op, left, right)
|
|
```
|
|
|
|
## 매 결정 기준
|
|
| 상황 | DSL Type |
|
|
|---|---|
|
|
| Domain expert reads | External DSL |
|
|
| Programmer power | Internal (fluent) |
|
|
| Configuration | YAML / HCL / TOML |
|
|
| Build | Make / Gradle / Bazel |
|
|
| Test BDD | Gherkin |
|
|
| LLM interface | NL → DSL |
|
|
| Constrained safety | External w/ verifier |
|
|
|
|
**기본값**: 매 internal DSL (TypeScript / Python fluent) for code-heavy. 매 external for non-dev.
|
|
|
|
## 🔗 Graph
|
|
- 변형: [[External-DSL]] · [[Internal-DSL]] · [[Fluent-API]]
|
|
- 응용: [[SQL]] · [[Terraform]] · [[Diagrams_as_Code]]
|
|
- Adjacent: [[Bounded-Contexts]] · [[Domain-Driven-Design]] · [[Articulateness]] · [[Be-Detailed]] · [[Container_Queries]] (CSS DSL)
|
|
|
|
## 🤖 LLM 활용
|
|
**언제**: 매 internal API design. 매 NL-to-DSL system. 매 IaC. 매 BDD.
|
|
**언제 X**: 매 simple imperative task.
|
|
|
|
## ❌ 안티패턴
|
|
- **DSL 의 Turing-complete 의 force**: 매 verification lose.
|
|
- **No documentation / examples**: 매 adoption fail.
|
|
- **Premature DSL** (small project): 매 over-engineering.
|
|
- **NL → DSL 의 unvalidated**: 매 destructive (e.g., DROP TABLE).
|
|
- **DSL leak host language**: 매 loose abstraction.
|
|
|
|
## 🧪 검증 / 중복
|
|
- Verified (Fowler "DSLs", PoEAA, ANTLR / Tree-sitter docs).
|
|
- 신뢰도 A.
|
|
- Related: [[Bounded-Contexts]] · [[Articulateness]] · [[Diagrams_as_Code]] · [[Custom-ESLint-Rules-Development]] · [[Be-Detailed]].
|
|
|
|
## 🕓 Changelog
|
|
| 날짜 | 변경 |
|
|
|---|---|
|
|
| 2026-05-08 | Phase 1 |
|
|
| 2026-05-10 | Manual cleanup — internal/external + 매 zod / Lark / Gherkin / NL-to-SQL / Terraform code |
|