MinhoJung 59 4
게시물 메뉴

한가해지면 이런 컨셉으로 언어 만들어 볼까  생각해봤어요.


HO - A Human-Oriented Programming Language

간결하고 직관적인 문법으로 데이터베이스 스키마와 비즈니스 로직을 정의하는 언어


📋 목차

  1. 개요

  2. 핵심 원칙

  3. 설치

  4. 개발 환경 설정

  5. 파일 구조

  6. 기본 문법

  7. Schema 파일

  8. Function 파일

  9. Type 파일

  10. Data 파일

  11. 예제

  12. CLI 명령어


개요

HO는 기존 프로그래밍 언어의 복잡함에서 벗어나 사람이 읽고 쓰기 편한 코드를 작성하기 위해 설계된 언어입니다.

특징

  • ✨ 직관적이고 명확한 문법

  • 📦 블록 기반 구조 ([[name]])

  • 🔗 ERD 스타일 관계 표현

  • 🎯 타입별 명확한 import 구분

  • 🚀 JavaScript로 변환되어 웹 생태계와 호환

  • ⚡ Go로 작성된 고속 컴파일러

  • 🎯 Bun 런타임 최적화

아키텍처

Transpiler 방식:

HO 코드 (.fn.ho, .schema.ho)
    ↓
Go 컴파일러 (ho)
    ↓
JavaScript (.js)
    ↓
Bun / Node.js / Deno
  • 컴파일러: Go로 작성 (빠른 컴파일, 단순한 배포)

  • 타겟: JavaScript (npm 생태계 활용)

  • 런타임: Bun 우선 (Node.js 호환)

  • 배포: 단일 바이너리 실행 파일

확장자

소스 파일:

  • .schema.ho - 데이터베이스 스키마

  • .fn.ho - 함수/비즈니스 로직

  • .type.ho - 커스텀 타입 정의

  • .data.ho - 데이터 (시드, 백업, 테스트)

  • .test.ho - 테스트 코드

  • .config.ho - 설정 파일

컴파일된 파일:

  • .schema.js - Schema → JavaScript

  • .fn.js - Function → JavaScript

  • .type.js - Type → JavaScript


핵심 원칙

1. 명확성 (Clarity)

  • 옵션이어도 명시적 표기 (-, [])

  • 모호함 없이 정확한 의도 전달

2. 직관성 (Intuitive)

  • 한눈에 구조 파악 가능

  • import 타입 명확히 구분

3. 간결성 (Concise)

  • 불필요한 키워드 제거

  • 최소한의 문법으로 최대 표현

4. 일관성 (Consistent)

  • 형식 순서 고정

  • 일관된 규칙 적용


파일 구조

project/
├── schemas/
│   ├── users.schema.ho
│   ├── posts.schema.ho
│   └── orders.schema.ho
├── functions/
│   ├── auth.fn.ho
│   ├── posts.fn.ho
│   └── orders.fn.ho
├── types/
│   ├── common.type.ho
│   └── enums.type.ho
├── data/
│   ├── users.seed.data.ho
│   ├── posts.seed.data.ho
│   └── backup.data.ho
├── tests/
│   ├── auth.test.ho
│   └── posts.test.ho
└── routes/
    └── (SvelteKit 스타일 폴더 구조)

기본 문법

블록과 배열

[[name]]    # 블록/정의 (테이블, 함수, 클래스 등)
[items]     # 배열/리스트

접근 제어

[[functionName]]    # public (기본)
[[_helperName]]     # private (_ prefix)

비동기 처리

# await 사용 시 자동으로 async 함수로 처리
body = {
  result = await someAsync()
}
return = result

함수 Return

# body와 return 분리
body = {
  # 모든 로직
  value = calculate()
}
return = value  # body 내 변수

# 또는 직접 값
body = {
  doSomething()
}
return = "success"  # 직접 값

Export

# 파일 내 모든 정의는 기본적으로 export
# export 키워드 불필요

Schema 파일

타입 정의

[[Type]]
type = serial, text, integer, boolean, timestamp, json

default? = Fn, {type}
constraints[]? = primary, unique, required, index
  • ? = 옵션 필드

  • Fn = 함수 (예: now())

  • {type} = 선언된 타입 사용 가능

테이블 정의

[[table_name]]
{key = type default? constraints[]?}
column_name = type default constraints

관계 표현

관계

표기법

설명

일대일

-

One-to-One

일대다

>

One-to-Many (Has Many)

다대일

<

Many-to-One (Belongs To)

다대다

<>

Many-to-Many

예제

[[Type]]
type = serial, text, integer, boolean, timestamp, json

default? = Fn, {type}
constraints[]? = primary, unique, required, index

# 일대일 관계
[[users]]-[[profiles.user_id]]
{key = type default? constraints[]?}
id = serial - [primary]
email = text - [unique required]
name = text null []
created_at = timestamp now() []

[[profiles]]
{key = type default? constraints[]?}
id = serial - [primary]
user_id = integer - [required]
bio = text null []
avatar = text null []
created_at = timestamp now() []

# 일대다 관계
[[users]>[[posts.author_id]]
{key = type default? constraints[]?}
id = serial - [primary]
name = text - [required]

[[posts]]
{key = type default? constraints[]?}
id = serial - [primary]
author_id = integer - [required]
title = text - [required]
content = text null []
published = boolean false []

# 다대다 관계 (중간 테이블 자동 생성)
[[posts]<>[[tags]]
{key = type default? constraints[]?}
id = serial - [primary]
title = text - [required]

[[tags]]
{key = type default? constraints[]?}
id = serial - [primary]
name = text - [unique required]

# Self-referencing
[[comments]>[[comments.parent_id]]
{key = type default? constraints[]?}
id = serial - [primary]
post_id = integer - [required]
author_id = integer - [required]
content = text - [required]
parent_id = integer null []

Function 파일

Import 구문

[[import type]]
./path/file.type [TypeName1, TypeName2]

[[import function]]
./path/file.fn [functionName1, functionName2]

[[import schema]]
./path/file.schema [tableName1, tableName2]

[[import constant]]
./path/file [CONSTANT_NAME]

[[import class]]
./path/file [ClassName]

[[import from js]]
./path/legacy [SomeLegacyClass]

함수 정의

[[functionName]]
params = (param1:type1, param2:type2)
returns = ReturnType

body = {
  # 함수 로직
  result = await someOperation()
}
return = result

원칙:

  • body = 과정 (모든 로직 실행)

  • return = 결과 (body 내 변수 또는 직접 값)

  • body에서 선언된 모든 변수는 return에서 접근 가능

  • return에 직접 값("success", 123 등) 작성 가능

클래스 정의

[[class ClassName]]
properties = {
  property1: type1
  property2: type2
}

[[ClassName.methodName]]
params = (param1:type1)
returns = ReturnType

body = {
  # 메서드 로직
  result = doSomething()
}
return = result

예제

[[import type]]
./$types [PageServerLoad]
./users.type [User, Profile]

[[import function]]
$lib/remote/posts.remote [getAdminStats]
./auth.fn [validateEmail]

[[import schema]]
./users.schema [users, profiles]

# Public 함수
[[load]]
params = ()
returns = PageServerLoad

body = {
  stats = await getAdminStats()
  validStats = _validateStats(stats)

}
return = {
  stats: validStats,
  error: null
}

# Private 함수
[[_validateStats]]
params = (stats:any)
returns = any

body = {
  if (!stats) return {}
}
return = stats

[[createUser]]
params = (email:text, name:text)
returns = User

body = {
  isValid = validateEmail(email)
  if (!isValid) return null

  user = await db.insert(users, {email, name})
  profile = await db.insert(profiles, {user_id: user.id})

}
return = user

# 클래스 예제
[[class EmailService]]
properties = {
  apiKey: text
  from: text
}

[[EmailService.send]]
params = (to:text, subject:text, body:text)
returns = boolean

body = {
  result = await api.post({
    to,
    subject,
    body,
    from: this.from
  })
}
return = result.success

Type 파일

(향후 정의 예정)

# Enum 타입
[[Enum.UserRole]]
values = [admin, user, guest]

# Custom 타입
[[Type.Email]]
base = text

pattern = ^[a-z0-9@.]+$

Data 파일

데이터 정의

[[format]]
columns = column1 column2 column3

[[data]]
value1 value2 value3
"value with space" value2 -

규칙:

  • 공백으로 구분

  • 공백 포함 시 "따옴표" 사용

  • null/빈값은 -

용도

  • 시드 데이터 - 개발용 초기 데이터

  • 백업 - 데이터베이스 백업

  • 테스트 픽스처 - 테스트용 데이터

  • Document DB - NoSQL 스타일 문서 저장

예제

시드 데이터:

# users.seed.data.ho

[[format]]
columns = id name email role created_at

[[data]]
1 admin admin@ho.dev admin 2025-01-01

2 demo demo@ho.dev user 2025-01-08

3 "Min Ho" minho@ho.dev admin -

공백 포함 데이터:

# posts.data.ho

[[format]]
columns = id title content author_id views

[[data]]
1 "Hello World" "This is my first post" 1 100

2 "HO Language" "Introducing HO programming language" 1 250

3 Introduction "Simple post" 2 50

빈값 처리:

# profiles.data.ho

[[format]]
columns = id user_id bio avatar website

[[data]]
1 1 "Developer from Korea" - -
2 2 - avatar.jpg "https://example.com"
3 3 "AI enthusiast" - -

CLI 명령어

# 시드 데이터 삽입
ho seed users.seed.data.ho

# 데이터베이스 백업
ho backup --output backup.data.ho

# 백업 복구
ho restore backup.data.ho

# 데이터 내보내기
ho export users --format ho --output users.data.ho

# 데이터 가져오기
ho import users.data.ho

예제

완전한 프로젝트 구조

blog-app/
├── schemas/
│   ├── users.schema.ho
│   ├── posts.schema.ho
│   └── comments.schema.ho
├── functions/
│   ├── auth.fn.ho
│   └── posts.fn.ho
├── types/
│   └── common.type.ho
└── routes/
    ├── +page.svelte
    └── api/
        └── posts/
            └── +server.ts (여기서 .fn.ho 사용)

users.schema.ho

[[Type]]
type = serial, text, integer, boolean, timestamp

default? = Fn, {type}
constraints[]? = primary, unique, required, index

[[users]]-[[profiles.user_id]]
{key = type default? constraints[]?}
id = serial - [primary]
email = text - [unique required]
name = text - [required]
password = text - [required]
created_at = timestamp now() []

[[profiles]]
{key = type default? constraints[]?}
id = serial - [primary]
user_id = integer - [required]
bio = text null []
avatar = text null []
website = text null []
created_at = timestamp now() []

posts.schema.ho

[[Type]]
type = serial, text, integer, boolean, timestamp

default? = Fn, {type}
constraints[]? = primary, unique, required, index

[[users]>[[posts.author_id]]
{key = type default? constraints[]?}
id = serial - [primary]

[[posts]<>[[tags]]
{key = type default? constraints[]?}
id = serial - [primary]
author_id = integer - [required]
title = text - [required]
content = text - [required]
published = boolean false []
views = integer 0 []
created_at = timestamp now() []
updated_at = timestamp now() []

[[tags]]
{key = type default? constraints[]?}
id = serial - [primary]
name = text - [unique required]
slug = text - [unique required]

[[posts]>[[comments.post_id]]
[[users]>[[comments.author_id]]
[[comments]>[[comments.parent_id]]
{key = type default? constraints[]?}
id = serial - [primary]
post_id = integer - [required]
author_id = integer - [required]
content = text - [required]
parent_id = integer null []
created_at = timestamp now() []

auth.fn.ho

[[import type]]
./users.type [User, CreateUserDto]

[[import schema]]
./users.schema [users]

[[import function]]
./utils.fn [hashPassword, validateEmail]

[[createUser]]
params = (email:text, name:text, password:text)
returns = User

body = {
  isValid = validateEmail(email)
  if (!isValid) throw "Invalid email"

  hashedPassword = await hashPassword(password)

  user = await db.insert(users, {
    email,
    name,
    password: hashedPassword
  })

}
return = user

[[login]]
params = (email:text, password:text)
returns = User

body = {
  user = await db.findOne(users, {email})
  if (!user) return null

  isValid = await _verifyPassword(password, user.password)
  if (!isValid) return null

}
return = user

[[_verifyPassword]]
params = (plain:text, hashed:text)
returns = boolean

body = {
  result = await bcrypt.compare(plain, hashed)
}
return = result

CLI 명령어

현재 구현된 명령어 (v0.1.0)

# 컴파일 (HO → JavaScript)
ho compile <file.fn.ho>
ho compile <file.schema.ho>

# 컴파일 + 실행
ho run <file.fn.ho>

# 버전 확인
ho version

# 도움말
ho help

사용 예제

# 함수 컴파일
ho compile functions/auth.fn.ho
# → functions/auth.fn.js 생성

# 스키마 컴파일
ho compile schemas/users.schema.ho
# → schemas/users.schema.js 생성

# 컴파일 + Bun 실행
ho run functions/hello.fn.ho

# 여러 파일 컴파일
for file in functions/*.fn.ho; do
  ho compile "$file"
done

계획 중인 명령어

# 프로젝트 초기화
ho init

# 개발 서버 실행 (watch 모드)
ho dev

# 프로덕션 빌드
ho build

# DB 마이그레이션
ho migrate

# 코드 생성 (schema → TypeScript types)
ho generate

# 문법 체크
ho check

# 포맷팅
ho format

# 데이터 관련
ho seed <file>              # 시드 데이터 삽입
ho backup --output <file>   # DB 백업
ho restore <file>           # 백업 복구
ho export <table>           # 테이블 데이터 내보내기
ho import <file>            # 데이터 가져오기

컴파일러 개발

Go 프로젝트 구조

ho-lang/
├── main.go              # CLI 진입점
├── compiler/
│   ├── parser.go        # HO 파싱
│   ├── generator.go     # JS 생성
│   └── types.go         # AST 정의
├── examples/
│   ├── hello.fn.ho
│   ├── auth.fn.ho
│   └── users.schema.ho
├── tests/
│   └── compiler_test.go
└── go.mod

빌드 & 테스트

# 개발 모드 실행
go run main.go compile examples/hello.fn.ho

# 빌드
go build -o ho main.go

# 테스트
go test ./...

# 크로스 컴파일
GOOS=linux GOARCH=amd64 go build -o ho-linux

GOOS=darwin GOARCH=arm64 go build -o ho-darwin

GOOS=windows GOARCH=amd64 go build -o ho.exe

로드맵

Phase 1: 기본 구현 ✅ (완료: 2025-01-08)

  • Go 기반 컴파일러

  • Function 파서 (.fn.ho)

  • JavaScript 코드 생성기

  • 기본 CLI (compile, run, version)

  • async/await 자동 감지

  • 여러 함수 지원

  • Schema 파서 (.schema.ho) - 진행 중

  • Import 구문 지원

Phase 2: 핵심 기능 (진행 중)

  • Schema 파일 완전 지원

  • 관계 표현 (>, <, <>)

  • Import 구문 처리

  • Type 파일 형식 확정

  • 에러 메시지 개선

  • VS Code 익스텐션 (문법 하이라이팅)

Phase 3: 생태계

  • DB 마이그레이션 도구

  • 문서 사이트

  • watch 모드 (ho dev)

  • 단일 바이너리 배포 (Bun compile)

  • npm 패키지 배포

Phase 4: 고도화

  • 타입 추론

  • 린터 & 포맷터

  • 플러그인 시스템

  • 다른 프레임워크 통합 (Next.js, Remix)

  • 퍼포먼스 최적화


기술 스택

컴파일러

  • 언어: Go 1.23+

  • 장점: 빠른 컴파일, 크로스 컴파일, 단일 바이너리

  • 패키지: 표준 라이브러리 (regexp, strings, os)

런타임

  • 타겟: JavaScript (ES2020+)

  • 우선순위: Bun > Node.js > Deno

  • 이유: Anthropic 인수, 고속 실행, npm 호환

개발 도구

  • 빌드: go build

  • 테스트: go test

  • 배포: GitHub Releases (멀티 플랫폼)


기여하기

HO는 오픈소스 프로젝트입니다. 기여를 환영합니다!

방법

  1. 이슈 제출: 버그 리포트, 기능 제안

  2. Pull Request: 코드 개선, 문서 수정

  3. 예제 공유: 실제 사용 사례

  4. 피드백: 문법, API 개선 의견

개발 가이드

# 저장소 클론
git clone https://github.com/minho/ho-lang.git

cd ho-lang

# 의존성 없음 (표준 라이브러리만 사용)
go mod tidy

# 개발 빌드
go build -o ho main.go

# 테스트
go test ./...

# 예제 실행
./ho compile examples/hello.fn.ho

관련 링크


라이선스

MIT (예정)


HO Language - Human-Oriented Programming

Created by Minho

 

 

 

Version: 0.1.0Released: 2025-01-08Compiler: Go 1.25+Runtime: Bun / Node.js