순서가 요점이다. 문법에 test_decl과 panic_expr을 넣고 파서는 안 고친 채로
대조 장치를 돌렸더니 즉시 잡혔다:
문장 300개 중 파서가 거부한 것 140개
[1] 선언 (fn, struct, enum, capability, const)이(가) 필요합니다 — test 발견
파서를 따라가게 하니 다시 0건. 문법과 구현이 어긋나는 상태가 관측 가능한
것이 되었다는 뜻이다.
panic:
- 키워드다. prelude가 없어 함수로 두면 쓸 때마다 import해야 한다
- effect가 아니다. 경계 검사 하나에 {Panic}이 호출자 전부로 전염되면
effect 절은 신호가 아니라 잡음이 된다
- Never는 어떤 타입 자리에도 놓인다. 없으면 panic을 match 팔에서 못 쓴다
- 언어 수준 recover 없음. 되감기 없음. 자원 해제 여부는 열어둔다
- 0으로 나누기, assert 실패가 이 하나로 모인다
test:
- 파라미터가 없어 capability를 받을 수 없고, 만들 문법도 없다. 그래서
effect-free임이 증명된다 — 관례가 아니라 검사다. 시험해 보니 실제로
"테스트는 effect를 수행할 수 없습니다"로 거부한다
- 일반 코드와 같은 타입/effect/move 검사를 받는다
- interface hash에서 제외 — 테스트를 고쳤다고 downstream이 재검사되면 안 된다
- 격리는 런타임의 일이다. 하나가 죽어도 나머지는 돈다
assert는 std/test.cool에 coollang으로 쓰였다 — panic 위의 설탕임이 코드로
보이고, std에서 본문이 있는 첫 함수가 됐다. 그 바람에 std/런타임 양방향
테스트가 걸렸고(본문 있는 함수에 런타임 구현을 요구했다), 그 구분을 넣었다.
samples/app/config.cool에 첫 테스트 넷.
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_panic 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_panic { msg; _ } ->
|
|
Buffer.add_string b "(panic ";
|
|
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
|