세 가지를 자율 결정으로 닫고 타입 검사까지 세웠다.
1. scope 부모 문법: scope 자식 = 부모 { ... }로 확정. 부모를 적지 않으면
자식의 부모가 "가장 가까운 스코프"가 되는데 그것이 정확히 ambient
authority다. 권한 사슬이 main의 루트 TaskScope부터 끊기지 않으려면 모든
자식이 부모를 이름으로 지목해야 한다. 이름 해소가 이 구멍을 잡아준 건이라,
주석 처리했던 중첩 예제를 되살렸다.
2. prelude: 타입 이름은 그 타입에 딸린 함수의 이름공간이다(String.len,
File.close). 정적 메서드를 위한 별도 문법을 두지 않는다.
3. 타입 검사: 이 모듈 안에서 아는 것만 검사한다. 외부 이름은 TUnknown이
되어 무엇과도 맞는다 — 모르는 것을 틀렸다고 말하지 않기 위해서다.
제네릭 해소는 호출 지점의 지역 unification이고 함수 하나를 넘지 않는다.
클로저 파라미터 타입은 기대 타입에서 읽어온다(양방향 검사, 로컬).
단계 소유권을 하나 정정했다. affinity는 타입 동등성의 일부가 아니다. 값이
affine인지는 무엇을 capture했는지로 정해지는 substructural 성질이고
move/affinity 검사가 소유한다. 타입 검사가 이걸 판정하려다 정당한 코드를
거부하는 것을 06에서 확인하고 unify에서 분리했다.
unify 버그 하나: 같은 미지수끼리 unify할 때 occurs check가 자기 자신을
발견해 실패하고 있었다. 02의 fold 호출에서 잡혔다.
09_type_errors.cool 추가 — 외부 타입이 하나도 없어 검사기가 TUnknown으로
빠져나갈 구석이 없는 파일이다. 18개 진단이 전부 잡히고, 첫 오류에서 멈추지
않고 모두 보고한다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZVDeU6KLuUVL3gs18Hm3E
472 lines
13 KiB
OCaml
472 lines
13 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 =
|
|
| T_named of { 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 { 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 }
|
|
| 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 }
|
|
|
|
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 { name; args; _ } ->
|
|
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 { name; args; _ } ->
|
|
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_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 ')'
|
|
|
|
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
|