철학 6번을 추가했다: 이름은 관례가 아니라 뜻에서 고른다 — 낯섦은 한 번 치르고 끝나지만 부정확함은 읽는 사람마다 매번 치른다. 판정 방법도 같이 적었다. 그 단어로 평범한 문장을 써 보고, 단어가 문장을 도우면 맞는 이름이고 싸우면 틀린 이름이다. "크래시는 복구하는 것이 아니라 조사하는 것이다" — 돕는다 "패닉은 복구할 수 없다" — 다른 언어에서는 할 수 있어 싸운다 panic의 자연어 뜻은 "갑작스러운 공포"다. 반응하는 쪽의 감정이지 결함에 대한 말이 아니다. 그리고 Go/Rust에서는 붙잡을 수 있어 이름이 거짓말을 한다. crash는 "계획 없이 갑자기 완전히 망가져 끝남"이고 복구의 함의가 없다 — 크래시는 복구하는 게 아니라 조사하는 것이다. 어휘의 출신도 이유가 됐다. panic+recover는 Go 전통이고 거기엔 감독이 없다. crash+supervision은 얼랭 전통이며, 우리가 만드는 것이 그쪽이다. 한국어 용어도 세 층으로 정리했다: 실패(Result) / 결함(crash) / 감독. 세 층이 세 가지 다른 기제로 규율된다 — 타입, 없음(발산), capability. "상황이 나쁨"은 결함이 아니라 실패다. 이 선을 안 그으면 crash가 게으름의 배출구가 된다. "오류"는 컴파일러 진단에만 쓴다. 얼랭 질문에 대한 답도 기록했다: 감독은 가져오고 비구조적 spawn은 안 가져온다. sc.spawn과 sup.spawn(sc, f)로 갈리며 문법 변경이 없다 — 실패를 삼키려면 Supervisor를 받았어야 하고 그것이 시그니처에 보인다. 개명은 문법을 먼저 고치고 대조 장치로 확인했다. 문장 500개, 파일 26개 모두 갈림 0건. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
499 lines
14 KiB
OCaml
499 lines
14 KiB
OCaml
(* 구문 트리. grammar.ebnf의 프로덕션과 일대일로 대응한다.
|
|
모든 노드가 위치를 들고 다닌다. *)
|
|
|
|
type pos = Token.pos
|
|
type lit = L_int of string | L_str of string | L_bool of bool
|
|
type eff_name = { cap : string; meth : string }
|
|
|
|
(* eff_atom: 변수 단독 또는 리터럴 집합. 합집합은 여기 없다 —
|
|
결과 위치에서만 eff_atom의 목록으로 나타난다. *)
|
|
type eff_atom = Eff_var of string | Eff_set of eff_name list
|
|
type eff_result = eff_atom list (* 합집합. 길이 1이면 단일 *)
|
|
|
|
type ty =
|
|
(* modl: 다른 모듈의 타입은 별칭으로 한정한다 (Shapes.Shape).
|
|
한정하지 않으면 이 모듈의 이름이다 — 암묵적으로 끌어오지 않는다. *)
|
|
| T_named of {
|
|
modl : string option;
|
|
name : string;
|
|
args : targ list;
|
|
pos : pos;
|
|
}
|
|
| T_fn of {
|
|
affine : bool;
|
|
params : ty list;
|
|
eff : eff_atom option;
|
|
ret : ty option;
|
|
pos : pos;
|
|
}
|
|
|
|
(* 제네릭 인자는 타입 또는 effect다. 맨 이름은 둘 다일 수 있으므로
|
|
파서는 타입으로 읽고 이름 해소가 판정한다. *)
|
|
and targ = TA_ty of ty | TA_eff of eff_atom
|
|
|
|
type pattern =
|
|
| P_wild of pos
|
|
| P_lit of lit * pos
|
|
| P_bind of string * pos
|
|
| P_ctor of {
|
|
modl : string option;
|
|
name : string;
|
|
args : pattern list;
|
|
pos : pos;
|
|
}
|
|
|
|
type unop = U_not | U_neg
|
|
|
|
type binop =
|
|
| B_or
|
|
| B_and
|
|
| B_eq
|
|
| B_ne
|
|
| B_lt
|
|
| B_le
|
|
| B_gt
|
|
| B_ge
|
|
| B_add
|
|
| B_sub
|
|
| B_mul
|
|
| B_div
|
|
| B_rem
|
|
|
|
type expr =
|
|
| E_lit of lit * pos
|
|
| E_ident of string * pos
|
|
| E_list of expr list * pos
|
|
| E_struct of { name : string; fields : (string * expr) list; pos : pos }
|
|
| E_closure of closure
|
|
| E_if of { cond : expr; then_ : block; else_ : expr option; pos : pos }
|
|
| E_match of { scrutinee : expr; arms : arm list; pos : pos }
|
|
| E_scope of { name : string; parent : string; body : block; pos : pos }
|
|
| E_block of block
|
|
| E_call of { callee : expr; args : expr list; pos : pos }
|
|
| E_field of { obj : expr; name : string; pos : pos }
|
|
| E_inst of { callee : expr; args : targ list; pos : pos }
|
|
| E_try of { inner : expr; pos : pos }
|
|
| E_unary of { op : unop; operand : expr; pos : pos }
|
|
(* 복구 불가능한 실패. 타입은 Never — 돌아오지 않으므로 어떤 자리에도 놓인다 *)
|
|
| E_crash of { msg : expr; pos : pos }
|
|
| E_binary of { op : binop; lhs : expr; rhs : expr; pos : pos }
|
|
|
|
and closure = {
|
|
cl_params : (string * ty option) list;
|
|
cl_eff : eff_atom option;
|
|
cl_ret : ty option;
|
|
cl_body : block;
|
|
cl_pos : pos;
|
|
}
|
|
|
|
and arm = { arm_pat : pattern; arm_body : expr; arm_pos : pos }
|
|
and block = { stmts : stmt list; block_pos : pos }
|
|
|
|
and stmt =
|
|
| S_let of {
|
|
mut_ : bool;
|
|
pat : pattern;
|
|
ty : ty option;
|
|
value : expr;
|
|
pos : pos;
|
|
}
|
|
| S_return of { value : expr option; pos : pos }
|
|
| S_assign of { place : expr; value : expr; pos : pos }
|
|
| S_expr of expr
|
|
|
|
type gen_param = { gp_name : string; gp_effect : bool; gp_pos : pos }
|
|
|
|
type param = {
|
|
p_own : bool;
|
|
p_mut : bool;
|
|
p_name : string;
|
|
p_ty : ty;
|
|
p_pos : pos;
|
|
}
|
|
|
|
type fn_decl = {
|
|
fn_name : string;
|
|
fn_gen : gen_param list;
|
|
fn_params : param list;
|
|
fn_eff : eff_result option;
|
|
fn_ret : ty option;
|
|
fn_body : block option;
|
|
fn_pos : pos;
|
|
}
|
|
|
|
type field = { f_name : string; f_ty : ty; f_pos : pos }
|
|
type variant = { v_name : string; v_args : ty list; v_pos : pos }
|
|
|
|
type item =
|
|
| I_import of { path : string; alias : string; pos : pos }
|
|
| I_reexport of { name : string; pos : pos }
|
|
| I_fn of { pub : bool; decl : fn_decl }
|
|
| I_struct of {
|
|
pub : bool;
|
|
copyable : bool;
|
|
name : string;
|
|
gen : gen_param list;
|
|
fields : field list;
|
|
pos : pos;
|
|
}
|
|
| I_enum of {
|
|
pub : bool;
|
|
name : string;
|
|
gen : gen_param list;
|
|
variants : variant list;
|
|
pos : pos;
|
|
}
|
|
| I_capability of {
|
|
pub : bool;
|
|
name : string;
|
|
methods : fn_decl list;
|
|
pos : pos;
|
|
}
|
|
| I_const of { pub : bool; name : string; ty : ty; value : expr; pos : pos }
|
|
(* 테스트. 파라미터가 없으므로 capability를 받을 수 없고, 그래서
|
|
effect-free임이 증명된다 — 관례가 아니라 검사다 *)
|
|
| I_test of { name : string; body : block; pos : pos }
|
|
|
|
type modul = { items : item list }
|
|
|
|
(* ------------------------------------------------------------------ *)
|
|
(* 출력 — 디버깅과 테스트용 s-식 *)
|
|
(* ------------------------------------------------------------------ *)
|
|
|
|
let buf_lit b = function
|
|
| L_int s -> Buffer.add_string b s
|
|
| L_str s -> Buffer.add_string b (Printf.sprintf "%S" s)
|
|
| L_bool v -> Buffer.add_string b (if v then "true" else "false")
|
|
|
|
let buf_list b f sep xs =
|
|
List.iteri
|
|
(fun i x ->
|
|
if i > 0 then Buffer.add_string b sep;
|
|
f x)
|
|
xs
|
|
|
|
let buf_eff_atom b = function
|
|
| Eff_var v -> Buffer.add_string b ("evar " ^ v)
|
|
| Eff_set names ->
|
|
Buffer.add_string b "eset";
|
|
List.iter
|
|
(fun { cap; meth } -> Buffer.add_string b (" " ^ cap ^ "." ^ meth))
|
|
names
|
|
|
|
let rec buf_ty b = function
|
|
| T_named { modl; name; args; _ } ->
|
|
let name = match modl with None -> name | Some m -> m ^ "." ^ name in
|
|
if args = [] then Buffer.add_string b name
|
|
else (
|
|
Buffer.add_string b ("(" ^ name);
|
|
List.iter
|
|
(fun a ->
|
|
Buffer.add_char b ' ';
|
|
buf_targ b a)
|
|
args;
|
|
Buffer.add_char b ')')
|
|
| T_fn { affine; params; eff; ret; _ } ->
|
|
Buffer.add_string b (if affine then "(affine-fn (" else "(fn (");
|
|
buf_list b (buf_ty b) " " params;
|
|
Buffer.add_char b ')';
|
|
(match eff with
|
|
| None -> ()
|
|
| Some e ->
|
|
Buffer.add_string b " [";
|
|
buf_eff_atom b e;
|
|
Buffer.add_char b ']');
|
|
(match ret with
|
|
| None -> ()
|
|
| Some t ->
|
|
Buffer.add_string b " -> ";
|
|
buf_ty b t);
|
|
Buffer.add_char b ')'
|
|
|
|
and buf_targ b = function
|
|
| TA_ty t -> buf_ty b t
|
|
| TA_eff e ->
|
|
Buffer.add_char b '[';
|
|
buf_eff_atom b e;
|
|
Buffer.add_char b ']'
|
|
|
|
let rec buf_pattern b = function
|
|
| P_wild _ -> Buffer.add_char b '_'
|
|
| P_lit (l, _) -> buf_lit b l
|
|
| P_bind (n, _) -> Buffer.add_string b n
|
|
| P_ctor { modl; name; args; _ } ->
|
|
let name = match modl with None -> name | Some m -> m ^ "." ^ name in
|
|
Buffer.add_string b ("(" ^ name);
|
|
List.iter
|
|
(fun p ->
|
|
Buffer.add_char b ' ';
|
|
buf_pattern b p)
|
|
args;
|
|
Buffer.add_char b ')'
|
|
|
|
let unop_name = function U_not -> "!" | U_neg -> "-"
|
|
|
|
let binop_name = function
|
|
| B_or -> "||"
|
|
| B_and -> "&&"
|
|
| B_eq -> "=="
|
|
| B_ne -> "!="
|
|
| B_lt -> "<"
|
|
| B_le -> "<="
|
|
| B_gt -> ">"
|
|
| B_ge -> ">="
|
|
| B_add -> "+"
|
|
| B_sub -> "-"
|
|
| B_mul -> "*"
|
|
| B_div -> "/"
|
|
| B_rem -> "%"
|
|
|
|
let rec buf_expr b = function
|
|
| E_lit (l, _) -> buf_lit b l
|
|
| E_crash { msg; _ } ->
|
|
Buffer.add_string b "(crash ";
|
|
buf_expr b msg;
|
|
Buffer.add_char b ')'
|
|
| E_ident (n, _) -> Buffer.add_string b n
|
|
| E_list (xs, _) ->
|
|
Buffer.add_string b "(list";
|
|
List.iter
|
|
(fun e ->
|
|
Buffer.add_char b ' ';
|
|
buf_expr b e)
|
|
xs;
|
|
Buffer.add_char b ')'
|
|
| E_struct { name; fields; _ } ->
|
|
Buffer.add_string b ("(struct " ^ name);
|
|
List.iter
|
|
(fun (n, e) ->
|
|
Buffer.add_string b (" (" ^ n ^ " ");
|
|
buf_expr b e;
|
|
Buffer.add_char b ')')
|
|
fields;
|
|
Buffer.add_char b ')'
|
|
| E_closure c ->
|
|
Buffer.add_string b "(closure (";
|
|
buf_list b
|
|
(fun (n, t) ->
|
|
Buffer.add_string b n;
|
|
match t with
|
|
| None -> ()
|
|
| Some t ->
|
|
Buffer.add_char b ':';
|
|
buf_ty b t)
|
|
" " c.cl_params;
|
|
Buffer.add_string b ") ";
|
|
buf_block b c.cl_body;
|
|
Buffer.add_char b ')'
|
|
| E_if { cond; then_; else_; _ } ->
|
|
Buffer.add_string b "(if ";
|
|
buf_expr b cond;
|
|
Buffer.add_char b ' ';
|
|
buf_block b then_;
|
|
(match else_ with
|
|
| None -> ()
|
|
| Some e ->
|
|
Buffer.add_char b ' ';
|
|
buf_expr b e);
|
|
Buffer.add_char b ')'
|
|
| E_match { scrutinee; arms; _ } ->
|
|
Buffer.add_string b "(match ";
|
|
buf_expr b scrutinee;
|
|
List.iter
|
|
(fun a ->
|
|
Buffer.add_string b " (";
|
|
buf_pattern b a.arm_pat;
|
|
Buffer.add_string b " => ";
|
|
buf_expr b a.arm_body;
|
|
Buffer.add_char b ')')
|
|
arms;
|
|
Buffer.add_char b ')'
|
|
| E_scope { name; parent; body; _ } ->
|
|
Buffer.add_string b ("(scope " ^ name ^ " = " ^ parent ^ " ");
|
|
buf_block b body;
|
|
Buffer.add_char b ')'
|
|
| E_block bl -> buf_block b bl
|
|
| E_call { callee; args; _ } ->
|
|
Buffer.add_string b "(call ";
|
|
buf_expr b callee;
|
|
List.iter
|
|
(fun e ->
|
|
Buffer.add_char b ' ';
|
|
buf_expr b e)
|
|
args;
|
|
Buffer.add_char b ')'
|
|
| E_field { obj; name; _ } ->
|
|
Buffer.add_string b "(. ";
|
|
buf_expr b obj;
|
|
Buffer.add_string b (" " ^ name ^ ")")
|
|
| E_inst { callee; args; _ } ->
|
|
Buffer.add_string b "(inst ";
|
|
buf_expr b callee;
|
|
List.iter
|
|
(fun a ->
|
|
Buffer.add_char b ' ';
|
|
buf_targ b a)
|
|
args;
|
|
Buffer.add_char b ')'
|
|
| E_try { inner; _ } ->
|
|
Buffer.add_string b "(? ";
|
|
buf_expr b inner;
|
|
Buffer.add_char b ')'
|
|
| E_unary { op; operand; _ } ->
|
|
Buffer.add_string b ("(" ^ unop_name op ^ " ");
|
|
buf_expr b operand;
|
|
Buffer.add_char b ')'
|
|
| E_binary { op; lhs; rhs; _ } ->
|
|
Buffer.add_string b ("(" ^ binop_name op ^ " ");
|
|
buf_expr b lhs;
|
|
Buffer.add_char b ' ';
|
|
buf_expr b rhs;
|
|
Buffer.add_char b ')'
|
|
|
|
and buf_block b { stmts; _ } =
|
|
Buffer.add_string b "(block";
|
|
List.iter
|
|
(fun s ->
|
|
Buffer.add_char b ' ';
|
|
buf_stmt b s)
|
|
stmts;
|
|
Buffer.add_char b ')'
|
|
|
|
and buf_stmt b = function
|
|
| S_let { mut_; pat; ty; value; _ } ->
|
|
Buffer.add_string b (if mut_ then "(let-mut " else "(let ");
|
|
buf_pattern b pat;
|
|
(match ty with
|
|
| None -> ()
|
|
| Some t ->
|
|
Buffer.add_char b ':';
|
|
buf_ty b t);
|
|
Buffer.add_char b ' ';
|
|
buf_expr b value;
|
|
Buffer.add_char b ')'
|
|
| S_return { value; _ } -> (
|
|
Buffer.add_string b "(return";
|
|
match value with
|
|
| None -> Buffer.add_char b ')'
|
|
| Some e ->
|
|
Buffer.add_char b ' ';
|
|
buf_expr b e;
|
|
Buffer.add_char b ')')
|
|
| S_assign { place; value; _ } ->
|
|
Buffer.add_string b "(set ";
|
|
buf_expr b place;
|
|
Buffer.add_char b ' ';
|
|
buf_expr b value;
|
|
Buffer.add_char b ')'
|
|
| S_expr e -> buf_expr b e
|
|
|
|
let buf_gen b gen =
|
|
if gen <> [] then (
|
|
Buffer.add_string b " <";
|
|
buf_list b
|
|
(fun g ->
|
|
Buffer.add_string b
|
|
(if g.gp_effect then g.gp_name ^ ":effects" else g.gp_name))
|
|
" " gen;
|
|
Buffer.add_char b '>')
|
|
|
|
let buf_fn b (d : fn_decl) =
|
|
Buffer.add_string b ("(fn " ^ d.fn_name);
|
|
buf_gen b d.fn_gen;
|
|
Buffer.add_string b " (";
|
|
buf_list b
|
|
(fun p ->
|
|
if p.p_own then Buffer.add_string b "own ";
|
|
if p.p_mut then Buffer.add_string b "mut ";
|
|
Buffer.add_string b (p.p_name ^ ":");
|
|
buf_ty b p.p_ty)
|
|
" " d.fn_params;
|
|
Buffer.add_char b ')';
|
|
(match d.fn_eff with
|
|
| None -> ()
|
|
| Some atoms ->
|
|
Buffer.add_string b " [";
|
|
buf_list b (fun a -> buf_eff_atom b a) " | " atoms;
|
|
Buffer.add_char b ']');
|
|
(match d.fn_ret with
|
|
| None -> ()
|
|
| Some t ->
|
|
Buffer.add_string b " -> ";
|
|
buf_ty b t);
|
|
(match d.fn_body with
|
|
| None -> Buffer.add_string b " decl"
|
|
| Some bl ->
|
|
Buffer.add_char b ' ';
|
|
buf_block b bl);
|
|
Buffer.add_char b ')'
|
|
|
|
let buf_item b = function
|
|
| I_import { path; alias; _ } ->
|
|
Buffer.add_string b (Printf.sprintf "(import %S as %s)" path alias)
|
|
| I_reexport { name; _ } -> Buffer.add_string b ("(reexport " ^ name ^ ")")
|
|
| I_fn { pub; decl } ->
|
|
if pub then Buffer.add_string b "pub ";
|
|
buf_fn b decl
|
|
| I_struct { pub; copyable; name; gen; fields; _ } ->
|
|
if pub then Buffer.add_string b "pub ";
|
|
if copyable then Buffer.add_string b "copyable ";
|
|
Buffer.add_string b ("(struct " ^ name);
|
|
buf_gen b gen;
|
|
List.iter
|
|
(fun f ->
|
|
Buffer.add_string b (" (" ^ f.f_name ^ " ");
|
|
buf_ty b f.f_ty;
|
|
Buffer.add_char b ')')
|
|
fields;
|
|
Buffer.add_char b ')'
|
|
| I_enum { pub; name; gen; variants; _ } ->
|
|
if pub then Buffer.add_string b "pub ";
|
|
Buffer.add_string b ("(enum " ^ name);
|
|
buf_gen b gen;
|
|
List.iter
|
|
(fun v ->
|
|
Buffer.add_string b (" (" ^ v.v_name);
|
|
List.iter
|
|
(fun t ->
|
|
Buffer.add_char b ' ';
|
|
buf_ty b t)
|
|
v.v_args;
|
|
Buffer.add_char b ')')
|
|
variants;
|
|
Buffer.add_char b ')'
|
|
| I_capability { pub; name; methods; _ } ->
|
|
if pub then Buffer.add_string b "pub ";
|
|
Buffer.add_string b ("(capability " ^ name);
|
|
List.iter
|
|
(fun m ->
|
|
Buffer.add_char b ' ';
|
|
buf_fn b m)
|
|
methods;
|
|
Buffer.add_char b ')'
|
|
| I_const { pub; name; ty; value; _ } ->
|
|
if pub then Buffer.add_string b "pub ";
|
|
Buffer.add_string b ("(const " ^ name ^ ":");
|
|
buf_ty b ty;
|
|
Buffer.add_char b ' ';
|
|
buf_expr b value;
|
|
Buffer.add_char b ')'
|
|
| I_test { name; body; _ } ->
|
|
Buffer.add_string b ("(test \"" ^ name ^ "\" ");
|
|
buf_block b body;
|
|
Buffer.add_char b ')'
|
|
|
|
let show_item item =
|
|
let b = Buffer.create 256 in
|
|
buf_item b item;
|
|
Buffer.contents b
|
|
|
|
let show_expr e =
|
|
let b = Buffer.create 128 in
|
|
buf_expr b e;
|
|
Buffer.contents b
|
|
|
|
let show_ty t =
|
|
let b = Buffer.create 64 in
|
|
buf_ty b t;
|
|
Buffer.contents b
|