theory Frontend imports Encoding begin fun finish :: "string \ string list \ string list" where "finish [] out = out" | "finish word out = rev word # out" fun lex :: "string \ string \ string list \ string list" where "lex [] word out = rev (finish word out)" | "lex (c # cs) word out = (if c = CHR '' '' \ c = CHR 0x09 \ c = CHR 0x0A \ c = CHR 0x0D then lex cs [] (finish word out) else if c = CHR ''('' \ c = CHR '')'' then lex cs [] ([c] # finish word out) else lex cs (c # word) out)" definition number :: "string \ nat option" where "number s = (if s = [] \ \ list_all (\c. 48 \ nat_of_char c \ nat_of_char c < 58) s then None else let n = foldl (\n c. min (2 ^ 64) (10 * n + nat_of_char c - 48)) 0 s in if n < 2 ^ 64 then Some n else None)" definition valid_name :: "string \ bool" where "valid_name s = (s \ [] \ list_all (\c. (97 \ nat_of_char c \ nat_of_char c \ 122)) s \ s \ ''let'' \ s \ ''if'')" fun lookup :: "string \ string list \ nat option" where "lookup name [] = None" | "lookup name (x # xs) = (if name = x then Some 0 else map_option Suc (lookup name xs))" lemma lookup_bound: "lookup name names = Some i \ i < length names" by (induction names arbitrary: i) (auto split: if_splits option.splits) fun operator :: "string \ operator option" where "operator s = (if s = ''+'' then Some Add else if s = ''-'' then Some Sub else if s = ''*'' then Some Mul else if s = ''and'' then Some And else if s = ''or'' then Some Or else if s = ''xor'' then Some Xor else if s = ''shl'' then Some Shl else if s = ''shr'' then Some Shr else if s = ''<'' then Some Less else None)" definition atom :: "string list \ string \ expression option" where "atom names s = (case lookup s names of Some i \ Some (Variable i) | None \ if s = ''x'' then Some (Input X) else if s = ''y'' then Some (Input Y) else if s = ''t'' then Some (Input T) else map_option (Literal \ word_of_nat) (number s))" lemma atom_scoped: "atom names s = Some e \ scoped (length names) e" by (auto simp: atom_def split: option.splits if_splits dest: lookup_bound) fun close :: "expression \ string list \ (expression \ string list) option" where "close e (s # rest) = (if s = '')'' then Some (e,rest) else None)" | "close e [] = None" lemma close_expression: "close e tokens = Some (a,rest) \ a = e" by (cases tokens) (auto split: if_splits) function parse :: "nat \ string list \ string list \ (expression \ string list) option" where "parse fuel names tokens = (if fuel = 0 then None else case tokens of [] \ None | s # rest \ if s \ ''('' then map_option (\e. (e,rest)) (atom names s) else case rest of [] \ None | op # args \ if op = ''let'' then (case args of [] \ None | name # body \ if \ valid_name name then None else Option.bind (parse (fuel - 1) names body) (\(a,tail). Option.bind (parse (fuel - 1) (name # names) tail) (\(b,tail). close (Let a b) tail))) else if op = ''if'' then Option.bind (parse (fuel - 1) names args) (\(c,tail). Option.bind (parse (fuel - 1) names tail) (\(a,tail). Option.bind (parse (fuel - 1) names tail) (\(b,tail). close (Cond c a b) tail))) else Option.bind (operator op) (\f. Option.bind (parse (fuel - 1) names args) (\(a,tail). Option.bind (parse (fuel - 1) names tail) (\(b,tail). close (Binary f a b) tail))))" by pat_completeness auto termination by (relation "measure (\(fuel,names,tokens). fuel)") auto declare parse.simps [simp del] theorem parse_scoped: "parse fuel names tokens = Some (e,rest) \ scoped (length names) e" proof (induction fuel arbitrary: names tokens e rest) case 0 then show ?case by (subst (asm) parse.simps) auto next case (Suc fuel) from Suc.prems show ?case by (subst (asm) parse.simps) (auto simp del: operator.simps dest: atom_scoped split: list.splits if_splits bind_splits option.splits prod.splits; drule close_expression; auto dest: Suc.IH) qed definition prepare :: "string \ expression option" where "prepare s = (if \ list_all (\c. nat_of_char c < 128) s then None else Option.bind (parse 128 [] (lex s [] [])) (\(e,rest). if rest = [] \ nodes e \ 4096 then let code = compile (optimise e) [] in if check code 0 = Some 1 \ demand (optimise e) \ 512 \ list_all encodable (native code) then Some e else None else None))" lemma prepare_scoped: "prepare s = Some e \ scoped 0 e" by (auto simp: prepare_def Let_def dest: parse_scoped split: if_splits bind_splits prod.splits) lemma prepare_bounded: "prepare s = Some e \ bounded 512 (compile (optimise e) []) 0" by (auto simp: prepare_def Let_def closed_demand optimise_scoped dest: parse_scoped split: if_splits bind_splits prod.splits) lemma prepare_encodable: "prepare s = Some e \ list_all encodable (native (compile (optimise e) []))" by (auto simp: prepare_def Let_def split: if_splits bind_splits prod.splits) theorem prepared_correct: "prepare source = Some e \ execute values (compile (optimise e) []) [] = Some [evaluate e [] values]" using compile_closed prepare_scoped by blast theorem prepared_native: "prepare source = Some e \ map_option observe (run (native (compile (optimise e) [])) s) = Some (inputs s, evaluate e [] (inputs s) # stack s)" using native_expression prepare_scoped by blast theorem prepared_bytes: "prepare source = Some e \ recover (length (native (compile (optimise e) []))) (executable e) = Some (native (compile (optimise e) []), [88,195])" unfolding executable_def using recover_encode prepare_encodable by blast theorem prepared_stacks: "prepare source = Some e \ \frames. snapshots values (compile (optimise e) []) [] = Some frames \ list_all (\s. length s \ 512) frames" using bounded_execution[where s="[]" and limit=512] prepare_bounded by auto function decimal :: "nat \ string" where "decimal n = (if n < 10 then [char_of_nat (48+n)] else decimal (n div 10) @ [char_of_nat (48+n mod 10)])" by pat_completeness auto termination by (relation "measure id") (auto simp: div_less_dividend) definition newline :: string where "newline = [CHR 0x0A]" definition artifact :: "string \ (bool \ string) \ string" where "artifact s = (case prepare s of None \ ((False,[]), ''invalid source or resource limit'' @ newline) | Some e \ let opt = optimise e; bytes = executable e in ((True, map char_of_nat bytes), decimal (nodes e) @ '' nodes -> '' @ decimal (nodes opt) @ '' nodes, '' @ decimal (length bytes) @ '' bytes, '' @ decimal (demand opt) @ '' stack words'' @ newline))" definition boundary :: "nat list" where "boundary = [0,1,63,64,65,127,255,256,2 ^ 31,2 ^ 32,2 ^ 63,2 ^ 64 - 1]" definition sample :: "nat \ input \ pixel" where "sample i = (\v. word_of_nat (if i < 12 then boundary ! ((i + (case v of X \ 0 | Y \ 3 | T \ 7)) mod 12) else case v of X \ (17*i) mod 257 | Y \ (29*i) mod 131 | T \ 13*i))" definition row :: "expression \ nat \ nat list" where "row e i = concat (map (\v. octets 8 (unat v)) [sample i X, sample i Y, sample i T, evaluate e [] (sample i)])" definition vectors :: "string \ bool \ string" where "vectors s = (case prepare s of None \ (False, ''invalid source or resource limit'' @ newline) | Some e \ (True, map char_of_nat (concat (map (row e) [0..<512]))))" definition reference :: "string \ string \ string \ string \ bool \ string" where "reference source x y t = (case (prepare source, number x, number y, number t) of (Some e,Some a,Some b,Some c) \ (True, decimal (unat (evaluate e [] (\v. word_of_nat (case v of X \ a | Y \ b | T \ c)))) @ newline) | _ \ (False,''invalid source or input'' @ newline))" end