Theory VIMP_Notation
theory VIMP_Notation
imports VIMP_Program VIMP_Grammar_Generated
begin
section ‹Concrete syntax›
text ‹
The ‹imp ⟦ ... ⟧› quotation produces an \<^typ>‹VIMP_Proc.com› without
HOL string quotes or qualified constructor names. The ‹imp› keyword distinguishes
command quotation from Pure premises; whole programs use the explicit ‹program› prefix.
Design is inspired by:
🌐‹https://awslabs.github.io/AutoCorrode/Unsorted/AutoCorrode/Micro_Rust_Examples.Basic_Micro_Rust.html›
and
🌐‹https://github.com/awslabs/AutoCorrode/blob/e234addc5e67f78cbff63defd24199578e8e1af3/Micro_Rust_Parsing_Frontend/Micro_Rust_Syntax.thy#L5›
Inside the bracket:
- bare identifiers become @{const V} literals (HOL string literals via @{type vname})
- numerals become @{const N}
- arithmetic:
+, -, *, /, % map to @{const Plus}, @{const Minus}, @{const Times},
@{const Div}, @{const Mod}
unary minus @{text "-n"} on numerals becomes @{const N} with a negative int
- comparisons:
<, <=, >, >=, ==, != evaluate to an @{typ int} 0/1
- constants:
true/false lower to @{const N} 1/@{const N} 0
Example:
@{verbatim [display]
"definition loop_prog :: VIMP_Proc.com where
\"loop_prog = imp ⟦
x = 0;
while (x < 20) { x = x + 1; }
⟧\""}
Whole-program syntax and its ‹imp_prog› lowering stay hand-written here,
matching the grammar's ‹special: program_structure› boundary; every other
nonterminal and lowering function comes from the generated
‹Vimp_Grammar_Tr› structure.
›
nonterminal imp2_funcs
syntax
"_IMP2" :: "imp2_stmts_opt ⇒ VIMP_Proc.com" ("imp ⟦ _ ⟧" [0] 1000)
"_PROGKW0" :: "imp2_funcs ⇒ imp_prog" ("program { _ }" [0] 1000)
"_PROGKW" :: "imp2_ids ⇒ imp2_funcs ⇒ imp_prog" ("program { global _ ; _ }" [0, 0] 1000)
"_funcs_nil" :: imp2_funcs ("")
"_funcs_cons0" :: "id_position ⇒ imp2_stmts_opt ⇒ imp2_funcs ⇒ imp2_funcs"
("fun _'(') { _ } _" [1000, 0, 0] 1000)
"_funcs_cons" :: "id_position ⇒ imp2_formals ⇒ imp2_stmts_opt ⇒ imp2_funcs ⇒ imp2_funcs"
("fun _'( _ ') { _ } _" [1000, 0, 0, 0] 1000)
parse_translation ‹
let
val c_proc_decl_ext = "VIMP_Proc.gproc_decl.gproc_decl_ext"
val c_Unity = "Product_Type.Unity"
val c_imp_prog = "VIMP_Program.mk_program"
val c_Pair = "Product_Type.Pair"
val K = Vimp_Grammar_Tr.K
val c_SKIP = Vimp_Grammar_Tr.c_SKIP
val c_Seq = Vimp_Grammar_Tr.c_Seq
val c_Return = Vimp_Grammar_Tr.c_Return
val c_Some = Vimp_Grammar_Tr.c_Some
val c_None = Vimp_Grammar_Tr.c_None
val c_Cons = Vimp_Grammar_Tr.c_Cons
val c_Nil = Vimp_Grammar_Tr.c_Nil
fun funcs_tr ctxt (Const ("_funcs_nil", _)) = []
| funcs_tr ctxt (Const ("_funcs_cons0", _) $ f $ pbody $ rest) =
(Vimp_Grammar_Tr.dest_id_position (SOME Markup.skolem) ctxt f,
[], SOME pbody, NONE) :: funcs_tr ctxt rest
| funcs_tr ctxt (Const ("_funcs_cons", _) $ f $ formals $ pbody $ rest) =
(Vimp_Grammar_Tr.dest_id_position (SOME Markup.skolem) ctxt f,
Vimp_Grammar_Tr.formals_of ctxt formals, SOME pbody, NONE)
:: funcs_tr ctxt rest
| funcs_tr _ t = raise TERM ("VIMP_Notation: funcs_tr", [t])
fun has_return (Const ("_stmt_return", _) $ _) = true
| has_return (Const ("_stmt_return0", _)) = true
| has_return (t $ u) = has_return t orelse has_return u
| has_return (Abs (_, _, b)) = has_return b
| has_return _ = false
fun check_distinct kind xs =
(case duplicates (op =) xs of
[] => ()
| ds => error ("VIMP program: duplicate " ^ kind ^ ": " ^ commas_quote ds))
fun check_no_global_formal_collision decls funcs =
List.app (fn (n, formals, _, _) =>
case filter (member (op =) decls) formals of
[] => ()
| bad => error ("VIMP program: " ^ quote n ^ " declares global(s) as formal(s): "
^ commas_quote bad)) funcs
fun mk_names [] = K c_Nil
| mk_names (n :: ns) = K c_Cons $ HOLogic.mk_literal n $ mk_names ns
fun mk_body_ret ctxt NONE NONE = K c_SKIP
| mk_body_ret ctxt (SOME c) NONE = Vimp_Grammar_Tr.stmts_opt_tr ctxt c
| mk_body_ret ctxt NONE (SOME e) = K c_Return $ (K c_Some $ Vimp_Grammar_Tr.exp_tr ctxt e)
| mk_body_ret ctxt (SOME c) (SOME e) =
K c_Seq $ Vimp_Grammar_Tr.stmts_opt_tr ctxt c
$ (K c_Return $ (K c_Some $ Vimp_Grammar_Tr.exp_tr ctxt e))
fun mk_proc_rep ctxt [] = K c_Nil
| mk_proc_rep ctxt ((p, formals, body, result) :: rest) =
K c_Cons
$ (K c_Pair $ HOLogic.mk_literal p
$ (K c_proc_decl_ext $ mk_names formals $ mk_body_ret ctxt body result $ K c_Unity))
$ mk_proc_rep ctxt rest
fun prog_tr ctxt decls funcs_t =
let
val funcs = funcs_tr ctxt funcs_t
val _ = check_distinct "procedure" (map (fn (n, _, _, _) => n) funcs)
val _ = check_distinct "declared global" decls
val _ = List.app (fn (n, formals, _, _) =>
check_distinct ("formal parameter of " ^ quote n) formals) funcs
val _ = check_no_global_formal_collision decls funcs
val (mains, procs) = List.partition (fn (n, _, _, _) => n = "main") funcs
val main_ast =
(case mains of
[("main", [], NONE, NONE)] => NONE
| [("main", [], SOME body, NONE)] =>
if has_return body then error "VIMP program: main may not return" else SOME body
| [("main", _, _, SOME _)] => error "VIMP program: main may not return"
| [("main", _, _, NONE)] => error "VIMP program: main must have no formals"
| [] => error "VIMP program: missing 'fun main() { ... }'"
| _ => error "VIMP program: more than one 'fun main()'")
val proc_rep = mk_proc_rep ctxt procs
val main = mk_body_ret ctxt main_ast NONE
val decl_globals = mk_names decls
in K c_imp_prog $ proc_rep $ main $ decl_globals end
in
[("_IMP2", fn ctxt => fn [t] => Vimp_Grammar_Tr.stmts_opt_tr ctxt t | _ => raise Match),
("_PROGKW0", fn ctxt => fn [fs] => prog_tr ctxt [] fs | _ => raise Match),
("_PROGKW",
fn ctxt => fn [g, fs] => prog_tr ctxt (Vimp_Grammar_Tr.names_of ctxt g) fs
| _ => raise Match)]
end
›
subsection ‹Smoke tests›
text ‹Four translations, each stated as the term it elaborates to: a branch and a check
through the generated statement translation, and a program with a global and one with a
procedure taking arguments through the whole-program translation hand-written here. The
theory stops loading if a translation stops elaborating or elaborates to something else,
which no theorem elsewhere would notice -- nothing in the proved pipeline reads concrete
syntax.›
lemma "imp ⟦ if (x < 10) { x = 0; } else { x = 1; } ⟧
= gcom.If () (Less (V STR ''x'') (N 10)) (Assign () STR ''x'' (N 0))
(Assign () STR ''x'' (N 1))"
by eval
lemma "(imp ⟦ __voblint_check(!(x == 0)); ⟧ :: com)
= Check () (0, 0) (exp.Not (exp.Eq (V STR ''x'') (N 0)))"
by eval
lemma "(program { global Gx; fun ping() { Gx = Gx + 1; } fun main() { ping(); } } :: imp_prog)
= ⦇proc_rep =
[(STR ''main'', ⦇formals = [], body = Call () None STR ''ping'' []⦈),
(STR ''ping'',
⦇formals = [],
body = Assign () STR ''Gx'' (Plus (V STR ''Gx'') (N 1))⦈)],
declared_global_vars = [STR ''Gx'']⦈"
by eval
lemma "(program { fun add(a, b) { skip; return a + b; } fun main() { r = add(1, 2); } } :: imp_prog)
= ⦇proc_rep =
[(STR ''main'',
⦇formals = [], body = Call () (Some STR ''r'') STR ''add'' [N 1, N 2]⦈),
(STR ''add'',
⦇formals = [STR ''a'', STR ''b''],
body =
Seq (SKIP ()) (Return () (Some (Plus (V STR ''a'') (V STR ''b''))))⦈)],
declared_global_vars = []⦈"
by eval
end