16장: CLI 참조 (CLI Reference)
이 장에서는 FunLang의 모든 커맨드라인 모드와 옵션을 다룹니다.
표현식 모드
--expr로 단일 표현식을 평가합니다:
$ fn --expr '1 + 2'
3
표현식은 파싱, 타입 검사, 평가를 거칩니다. 결과는 표준 출력으로 출력됩니다.
다양한 결과 타입:
$ fn --expr '42'
42
$ fn --expr '"hello"'
"hello"
$ fn --expr 'true'
true
$ fn --expr '[1; 2; 3]'
[1; 2; 3]
$ fn --expr '(1, "hello", true)'
(1, "hello", true)
$ fn --expr '()'
()
$ fn --expr 'fun x -> x + 1'
<function>
표현식 모드에서는 let ... in 구문으로 로컬 바인딩을 사용합니다:
$ fn --expr 'let x = 5 in let y = 10 in x + y'
15
표현식 모드는 한 줄만 지원하며, 들여쓰기를 지원하지 않습니다.
파일 모드
프로그램 파일을 평가합니다:
$ cat hello.l3
let greeting = "hello"
let result = greeting + " world"
$ fn hello.l3
"hello world"
파일 모드는 들여쓰기 기반 문법을 지원합니다. 마지막 let 바인딩의 값이
출력됩니다. 최종 결과 전에 부수 효과를 실행하려면 let _ =를 사용합니다:
$ cat greet.l3
let _ = println "starting..."
let name = "world"
let result = "hello " + name
$ fn greet.l3
starting...
"hello world"
파일 모드는 FunLang의 모든 기능을 지원합니다: 타입 선언, 모듈, 예외, 들여쓰기 기반 패턴 매칭, 여러 줄 표현식.
.l3 확장자는 관례이며 강제되지 않습니다 – 어떤 파일 이름이든 사용 가능합니다.
AST 출력
--emit-ast로 파싱된 추상 구문 트리를 확인합니다:
$ fn --emit-ast --expr '1 + 2'
Add (Number 1, Number 2)
$ fn --emit-ast --expr 'fun x -> x + 1'
Lambda ("x", Add (Var "x", Number 1))
$ fn --emit-ast --expr 'let x = 5 in x + 1'
Let ("x", Number 5, Add (Var "x", Number 1))
파일 모드에서는 각 선언이 표시됩니다:
$ cat ast_demo.l3
let x = 42
let add a b = a + b
$ fn --emit-ast ast_demo.l3
LetDecl ("x", Number 42)
LetDecl ("add", Lambda ("a", Lambda ("b", Add (Var "a", Var "b"))))
파싱 문제를 디버깅하는 데 유용합니다 – 코드가 의도한 대로 파싱되었는지 확인합니다.
타입 출력
--emit-type으로 추론된 타입을 확인합니다:
$ fn --emit-type --expr '1 + 2'
int
$ fn --emit-type --expr 'fun x -> x + 1'
int -> int
$ fn --emit-type --expr '"hello"'
string
파일 모드에서는 사용자 정의 최상위 바인딩의 타입이 모두 표시됩니다 (알파벳순 정렬, 내장 및 Prelude 바인딩 제외):
$ cat types_demo.l3
let x = 42
let greet name = "hello " + name
let result = greet "world"
$ fn --emit-type types_demo.l3
greet : string -> string
result : string
x : int
다형 타입은 타입 변수를 표시합니다:
$ fn --emit-type --expr 'fun x -> x'
'a -> 'a
$ fn --emit-type --expr 'fun f -> fun x -> f x'
('a -> 'b) -> 'a -> 'b
토큰 출력
--emit-tokens로 렉서의 원시 토큰을 확인합니다 (IndentFilter 적용 전):
$ fn --emit-tokens --expr '1 + 2'
NUMBER(1) PLUS NUMBER(2) EOF
파일 모드에서는 NEWLINE(n) 토큰이 각 줄의 들여쓰기 수준을 나타냅니다:
$ cat tokens_demo.l3
let result =
if true then 1
else 2
$ fn --emit-tokens tokens_demo.l3
LET IDENT(result) EQUALS NEWLINE(4) IF TRUE THEN NUMBER(1) NEWLINE(4) ELSE NUMBER(2) NEWLINE(0) EOF
필터된 토큰 출력
--emit-filtered-tokens로 IndentFilter를 거친 토큰을 확인합니다. NEWLINE(n)이 INDENT/DEDENT/IN/SEMICOLON으로 변환된 결과를 볼 수 있습니다:
$ cat filtered_demo.l3
module M =
let x = 1
let y = 2
let result = M.x + M.y
$ fn --emit-filtered-tokens filtered_demo.l3
MODULE IDENT(M) EQUALS INDENT LET IDENT(x) EQUALS NUMBER(1) LET IDENT(y) EQUALS NUMBER(2) DEDENT LET IDENT(result) EQUALS IDENT(M) DOT IDENT(x) PLUS IDENT(M) DOT IDENT(y) EOF
들여쓰기 관련 파싱 문제를 디버깅할 때 유용합니다. --emit-tokens는 렉서의 원시 출력을 보여주고, --emit-filtered-tokens는 파서가 실제로 받는 토큰 스트림을 보여줍니다.
타입 체크만 수행
--check는 파일을 타입 체크하지만 실행하지 않습니다. open으로 임포트된 파일도 모두 포함됩니다:
$ cat good.l3
let x = 1 + 2
let y = x * 3
$ fn --check good.l3
OK (0 warnings)
타입 오류가 있으면 에러 메시지만 출력하고 종료합니다:
$ cat bad.l3
let x = 1 + "hello"
$ fn --check bad.l3
error[E0301]: Type mismatch: expected int but got string
--> bad.l3:1:6-18
= hint: Check that all branches of your expression return the same type
CI/CD 파이프라인이나 에디터 통합에서 코드를 실행하지 않고 검증할 때 유용합니다.
의존성 트리
--deps는 파일의 임포트 의존성 트리를 출력합니다:
$ fn --deps main.l3
main.l3
lib/math.fun
lib/helpers.fun
utils/format.fun
lib/math.fun (cached)
순환 의존성이 있으면 감지하여 보고합니다.
Prelude 경로 설정
--prelude로 Prelude 디렉토리 경로를 지정합니다:
$ fn --prelude /path/to/my/Prelude myfile.l3
Prelude 경로 우선순위: --prelude > LANGTHREE_PRELUDE 환경 변수 > funproj.toml 설정 > 자동 탐색.
프로젝트 빌드
funproj.toml 파일이 있는 디렉토리에서 build와 test 서브커맨드를 사용할 수 있습니다:
$ fn build # 모든 [[executable]] 타겟 타입 체크
$ fn build myapp # 특정 타겟만 타입 체크
$ fn test # 모든 [[test]] 타겟 실행
$ fn test mytest # 특정 테스트만 실행
funproj.toml 형식:
[project]
name = "myproject"
prelude = "Prelude"
[[executable]]
name = "myapp"
main = "src/main.l3"
[[test]]
name = "mytest"
main = "tests/test.l3"
REPL
인자 없이 langthree를 실행하여 대화형 세션을 시작합니다:
$ fn
FunLang REPL v14.0
Type :help for commands, #quit or Ctrl+D to exit.
fn> 1 + 2
- : int = 3
fn> let x = 42
val x : int = 42
fn> let y = x * 2
val y : int = 84
fn> x + y
- : int = 126
fn> #quit
영속적 바인딩
REPL에서 let 바인딩은 다음 줄에서도 유지됩니다. 타입 선언, 모듈, 타입 클래스 인스턴스도 영속적입니다:
fn> type Color = | Red | Green | Blue
type defined
fn> deriving Show Color
deriving applied
fn> show Green
- : string = "Green"
REPL 명령
| 명령 | 설명 |
|---|---|
:type <expr> | 표현식의 추론된 타입만 표시 (평가하지 않음) |
:load <file> | 파일을 REPL 환경에 로드 |
:help | 사용 가능한 명령 목록 |
#quit / #exit | REPL 종료 (Ctrl+D도 가능) |
fn> :type fun x y -> x + y
int -> int -> int
fn> :type map
('a -> 'b) -> 'a list -> 'b list
출력 형식
- 표현식:
- : int = 3(타입과 값을 함께 표시) - 선언:
val x : int = 42(이름, 타입, 값) - 타입 정의:
type defined - 타입 클래스:
typeclass defined/instance defined
진단 모드 요약
| 플래그 | 설명 | 표현식 | 파일 |
|---|---|---|---|
| (없음) | 평가 후 결과 출력 | N/A | 마지막 바인딩의 값 |
--expr | 표현식 평가 | 표현식 결과 | N/A |
--emit-ast | 파싱된 AST 표시 | 표현식의 AST | 모든 선언 |
--emit-type | 추론된 타입 표시 | 표현식의 타입 | 모든 바인딩 타입 |
--emit-tokens | 렉서 원시 토큰 표시 | 원시 토큰 | NEWLINE(n) 포함 |
--emit-filtered-tokens | 필터된 토큰 표시 | 필터된 토큰 | INDENT/DEDENT/IN 포함 |
--check | 타입 체크만 (실행 안 함) | N/A | 오류/경고 출력 |
--deps | 의존성 트리 표시 | N/A | 임포트 트리 |
--prelude | Prelude 경로 지정 | 적용 | 적용 |
build [name] | 프로젝트 타입 체크 | N/A | funproj.toml |
test [name] | 프로젝트 테스트 실행 | N/A | funproj.toml |
| (인자 없음) | REPL 대화형 세션 | 줄 단위 평가 | N/A |
진단 플래그는 --expr 또는 파일 이름과 함께 사용합니다:
$ fn --emit-type --expr '1 + 2'
int
$ fn --emit-type types_demo.l3
greet : string -> string
result : string
x : int
오류 메시지
타입 오류에는 오류 코드, 소스 위치, 소스 스니펫, 힌트가 포함됩니다. 파일 모드에서는 해당 소스 라인과 ^^^ 밑줄이 표시됩니다:
$ cat type_error.l3
let x = 1
let y = x + "hello"
$ fn type_error.l3
error[E0301]: Type mismatch: expected int but got string
--> type_error.l3:2:6-19
|
2 | let y = x + "hello"
| ^^^^^^^^^^^^^
= hint: Check that all branches of your expression return the same type
표현식 모드에서는 소스 파일이 없으므로 위치만 표시됩니다:
$ fn --expr '"hello" + 1'
error[E0301]: Type mismatch: expected string but got int
--> <expr>:1:6-11
= hint: Check that all branches of your expression return the same type
“Did you mean?” 제안
변수명이나 함수명에 오타가 있으면 유사한 이름을 제안합니다:
$ cat typo.l3
let x = 1
let result = prnt (to_string x)
$ fn typo.l3
error[E0303]: Unbound variable: prnt
--> typo.l3:2:11-17
|
2 | let result = prnt (to_string x)
| ^^^^^^
= hint: Did you mean 'print'?
변수, 생성자, 모듈, 타입 클래스 이름에 대해 제안이 동작합니다.
파싱 오류
파싱 오류에는 예상하지 못한 토큰과 소스 위치가 포함됩니다:
$ cat parse_err.l3
let f x = = y
$ fn parse_err.l3
Error: parse error: unexpected EQUALS at parse_err.l3:1:8
|
1 | let f x = = y
| ^
표현식 모드의 파싱 오류는 위치 정보 없이 표시됩니다:
$ fn --expr '1 +'
Error: parse error
경고
FunLang는 잠재적 문제에 대해 경고를 발생시킵니다. 경고는 실행을 막지 않으며, 프로그램은 그대로 실행됩니다.
W0001: 불완전한 패턴 매칭
match 표현식이 모든 생성자를 다루지 않을 때 발생합니다:
$ cat incomplete.l3
type Color =
| Red
| Green
| Blue
let result =
match Red with
| Red -> 1
| Green -> 2
$ fn incomplete.l3
Warning: warning[W0001]: Incomplete pattern match. Missing cases: Blue
--> incomplete.l3:6:4-8:16
|
6 | match Red with
| ^^^^^^^^^^^^^^
= hint: Add the missing cases or a wildcard pattern '_' to cover all values
1
W0002: 중복 패턴
패턴 절에 도달할 수 없을 때 발생합니다:
$ cat redundant.l3
type Color =
| Red
| Green
| Blue
let result =
match Red with
| Red -> 1
| Green -> 2
| Blue -> 3
| Red -> 4
$ fn redundant.l3
Warning: warning[W0002]: Redundant pattern in clause 4. This case will never be reached.
--> redundant.l3:6:4-11:10
|
6 | match Red with
| ^^^^^^^^^^^^^^
= hint: Remove the unreachable pattern
1
W0003: 불완전한 예외 핸들러
try ... with 블록이 가능한 모든 예외를 처리하지 않을 때 발생합니다:
$ cat handler.l3
exception MyError of string
let result =
try
raise (MyError "oops")
with
| MyError msg -> msg
$ fn handler.l3
Warning: warning[W0003]: Non-exhaustive exception handler: not all exceptions are handled; consider adding a catch-all handler
--> :0:0-1:0
= hint: Add a catch-all handler or handle all possible exceptions
"oops"
이 경고를 없애려면 | _ -> ... 포괄 핸들러를 추가하세요.