ligo/test/proto_alpha/test_michelson_parser.ml
2017-12-08 11:08:13 +00:00

418 lines
18 KiB
OCaml

(**************************************************************************)
(* *)
(* Copyright (c) 2014 - 2017. *)
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* *)
(* All rights reserved. No warranty, explicit or implicit, provided. *)
(* *)
(**************************************************************************)
module Helpers = Proto_alpha_helpers
module Assert = Helpers.Assert
let known_ok_tez_litterals =
[ 0L, "0" ;
10L, "0.000,01" ;
100L, "0.000,1" ;
1_000L, "0.001" ;
10_000L, "0.01" ;
100_000L, "0.1" ;
1_000_000L, "1" ;
10_000_000L, "10" ;
100_000_000L, "100" ;
1_000_000_000L, "1,000" ;
10_000_000_000L, "10,000" ;
100_000_000_000L, "100,000" ;
1_000_000_000_000L, "1,000,000" ;
1_000_000_000_001L, "1,000,000.000,001" ;
1_000_000_000_010L, "1,000,000.000,01" ;
1_000_000_000_100L, "1,000,000.000,1" ;
1_000_000_001_000L, "1,000,000.001" ;
1_000_000_010_000L, "1,000,000.01" ;
1_000_000_100_000L, "1,000,000.1" ;
123_123_123_123_123_123L, "123,123,123,123.123,123" ;
999_999_999_999_999_999L, "999,999,999,999.999,999" ]
let known_bad_tez_litterals =
[ "10000" ;
"1,0000" ;
"0.0000,1" ;
"0.00,1" ;
"0,1" ;
"0.0001" ;
"HAHA" ;
"0.000,000,1" ;
"9,999,999,999,999.999,999"]
let test_known_tez_litterals () =
List.iter
(fun (v, s) ->
let vv = Tez_repr.of_mutez v in
let vs = Tez_repr.of_string s in
let vv = match vv with None -> Assert.fail_msg "could not unopt %Ld" v | Some vv -> vv in
let vs = match vs with None -> Assert.fail_msg "could not unopt %s" s | Some vs -> vs in
Assert.equal ~prn:Tez_repr.to_string vv vs ;
Assert.equal ~prn:(fun s -> s) (Tez_repr.to_string vv) s)
known_ok_tez_litterals ;
List.iter
(fun s ->
let vs = Tez_repr.of_string s in
Assert.is_none ~msg:("Unexpected successful parsing of " ^ s) vs)
known_bad_tez_litterals ;
return ()
let test_random_tez_litterals () =
for _ = 0 to 100_000 do
let v = Random.int64 12L in
let vv = Tez_repr.of_mutez v in
let vv = match vv with None -> Assert.fail_msg "could not unopt %Ld" v | Some vv -> vv in
let s = Tez_repr.to_string vv in
let vs = Tez_repr.of_string s in
Assert.is_some ~msg:("Could not parse " ^ s ^ " back") vs ;
match vs with
| None -> assert false
| Some vs ->
let rev = Tez_repr.to_int64 vs in
Assert.equal ~prn:Int64.to_string ~msg:(Tez_repr.to_string vv) v rev
done ;
return ()
open Tezos_micheline
open Micheline
let zero_loc = Micheline_parser.location_zero
let prn expr =
expr |>
Micheline_printer.printable (fun s -> s) |>
Format.asprintf "%a" Micheline_printer.print_expr
let assert_expands original expanded =
let { Michelson_v1_parser.expanded = expansion }, errors =
let source = prn (Micheline.strip_locations original) in
Michelson_v1_parser.expand_all ~source ~original in
let expanded = Micheline.strip_locations expanded in
let expansion = Michelson_v1_primitives.strings_of_prims expansion in
match errors with
| [] ->
Assert.equal ~prn expansion expanded ;
ok ()
| errors -> Error errors
let left_branch = Seq(zero_loc, [ Prim(zero_loc, "SWAP", [], None) ], None)
let right_branch = Seq(zero_loc, [ ], None)
let test_expansion () =
assert_expands (Prim (zero_loc, "CAAR", [], None))
(Seq (zero_loc,
[(Prim (zero_loc, "CAR", [], None));
(Prim (zero_loc, "CAR", [], None)) ],
None)) >>? fun () ->
assert_expands (Prim (zero_loc, "CAAR", [], Some "annot"))
(Seq (zero_loc,
[(Prim (zero_loc, "CAR", [], None));
(Prim (zero_loc, "CAR", [], Some "annot")) ],
None)) >>? fun () ->
let car = Prim (zero_loc, "CAR", [], Some "annot") in
assert_expands car car >>? fun () ->
let arg = [ Seq (zero_loc, [ car ], None) ] in
assert_expands
(Prim (zero_loc, "DIP", arg, Some "new_annot"))
(Prim (zero_loc, "DIP", arg, Some "new_annot")) >>? fun () ->
assert_expands
(Prim (zero_loc, "DIIP", arg, None))
(Seq (zero_loc,
[ Prim (zero_loc, "DIP",
[ (Seq (zero_loc,
[ Prim (zero_loc, "DIP", arg, None) ],
None)) ],
None) ],
None)) >>? fun () ->
assert_expands
(Prim (zero_loc, "DIIIP", arg, None))
(Seq (zero_loc,
[ Prim (zero_loc, "DIP",
[ (Seq (zero_loc,
[ Prim (zero_loc,
"DIP",
[ (Seq (zero_loc,
[ Prim (zero_loc, "DIP", arg, None) ],
None)) ],
None) ],
None)) ],
None) ],
None)) >>? fun () ->
assert_expands
(Prim (zero_loc, "DUUP", [], None))
(Seq (zero_loc,
[ Prim (zero_loc, "DIP", [ Seq (zero_loc, [ Prim (zero_loc, "DUP", [], None) ], None) ], None) ;
Prim (zero_loc, "SWAP", [], None) ], None)) >>? fun () ->
assert_expands
(Prim (zero_loc, "DUUUP", [], None))
(Seq (zero_loc,
[ Prim (zero_loc, "DIP",
[ Seq (zero_loc, [
Prim (zero_loc, "DIP", [
Seq (zero_loc, [ Prim (zero_loc, "DUP", [], None) ], None)],
None);
Prim (zero_loc, "SWAP", [], None) ],
None) ],
None) ;
Prim (zero_loc, "SWAP", [], None) ], None)) >>? fun () ->
let assert_compare_macro prim_name compare_name =
assert_expands
(Prim (zero_loc, prim_name, [], None))
(Seq (zero_loc,
[ Prim (zero_loc, "COMPARE", [], None) ;
Prim (zero_loc, compare_name, [], None) ], None)) in
let assert_compare_if_macro prim_name compare_name =
assert_expands
(Prim (zero_loc, prim_name,
[ left_branch ; right_branch ],
None))
(Seq (zero_loc, [ Prim(zero_loc, "COMPARE", [], None);
Prim(zero_loc, compare_name, [], None);
Prim (zero_loc, "IF", [ left_branch ; right_branch ], None) ], None)) in
assert_compare_macro "CMPEQ" "EQ" >>? fun () ->
assert_compare_macro "CMPNEQ" "NEQ" >>? fun () ->
assert_compare_macro "CMPLT" "LT" >>? fun () ->
assert_compare_macro "CMPLE" "LE" >>? fun () ->
assert_compare_macro "CMPGT" "GT" >>? fun () ->
assert_compare_macro "CMPGE" "GE" >>? fun () ->
assert_compare_if_macro "IFCMPEQ" "EQ" >>? fun () ->
assert_compare_if_macro "IFCMPNEQ" "NEQ" >>? fun () ->
assert_compare_if_macro "IFCMPLT" "LT" >>? fun () ->
assert_compare_if_macro "IFCMPLE" "LE" >>? fun () ->
assert_compare_if_macro "IFCMPGT" "GT" >>? fun () ->
assert_compare_if_macro "IFCMPGE" "GE" >>? fun () ->
assert_expands (Prim (zero_loc, "ASSERT_LEFT", [], None))
(Seq (zero_loc, [ Prim (zero_loc, "IF_LEFT",
[ Seq (zero_loc, [ ], None) ;
Seq (zero_loc, [ Prim(zero_loc, "FAIL", [], None) ], None) ],
None) ], None)) >>? fun () ->
assert_expands (Prim (zero_loc, "ASSERT_RIGHT", [], None))
(Seq (zero_loc, [ Prim (zero_loc, "IF_LEFT",
[ Seq (zero_loc, [ Prim(zero_loc, "FAIL", [], None) ], None) ;
Seq (zero_loc, [ ], None) ],
None) ], None)) >>? fun () ->
assert_expands (Prim (zero_loc, "IF_RIGHT", [ left_branch ; right_branch ], None))
(Seq (zero_loc, [ Prim (zero_loc, "IF_LEFT", [ right_branch ; left_branch ], None) ], None)) >>? fun () ->
assert_expands (Prim (zero_loc, "IF_SOME", [ left_branch ; right_branch ], None))
(Seq (zero_loc, [ Prim (zero_loc, "IF_NONE", [ right_branch ; left_branch ], None) ], None)) >>? fun () ->
assert_expands
(Prim (zero_loc, "PAIR", [], None))
(Prim (zero_loc, "PAIR", [], None)) >>? fun () ->
assert_expands
(Prim (zero_loc, "PAAIR", [], None))
(Seq (zero_loc,
[Prim
(zero_loc,
"DIP",
[Seq (zero_loc, [Prim
(zero_loc, "PAIR", [], None)],
None)],
None)],
None)) >>? fun () ->
assert_expands
(Prim (zero_loc, "PAAIAIR", [], None))
(Seq (zero_loc, [Prim
(zero_loc,
"DIP",
[Seq
(zero_loc,
[Prim
(zero_loc,
"PAIR", [], None)],
None)],
None);
Prim
(zero_loc,
"PAIR", [], None)],
None))
let assert_unexpansion_consistent original =
let { Michelson_v1_parser.expanded }, errors =
let source = prn (Micheline.strip_locations original) in
Michelson_v1_parser.expand_all ~source ~original in
match errors with
| _ :: _ -> Error errors
| [] ->
let { Michelson_v1_parser.unexpanded } =
Michelson_v1_printer.unparse_expression expanded in
Assert.equal ~prn unexpanded (Micheline.strip_locations original) ;
ok ()
let test_unexpansion_consistency () =
assert_unexpansion_consistent (Prim (zero_loc, "PAAAIAIR", [], None)) >>? fun () ->
assert_unexpansion_consistent
(Prim (zero_loc, "DIIIP", [ Seq (zero_loc, [ Prim (zero_loc, "DROP", [], None) ], None) ], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "SET_CAR", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "SET_CDR", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "DUP", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "DUUP", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "DUUUP", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "DUUUUP", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "DUUUUUP", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "ASSERT_EQ", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "ASSERT_NEQ", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "ASSERT_LT", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "ASSERT_LE", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "ASSERT_GT", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "ASSERT_GE", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "ASSERT_NONE", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "ASSERT_SOME", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "ASSERT_LEFT", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "ASSERT_RIGHT", [], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "IF_RIGHT", [ left_branch ; right_branch], None)) >>? fun () ->
assert_unexpansion_consistent (Prim (zero_loc, "IF_SOME", [ left_branch ; right_branch], None))
let test_lexing () =
let open Micheline_parser in
let assert_tokenize_result source expected =
match tokenize source with
| tokens, [] ->
let tokens =
List.map (fun x -> x.token) tokens in
Assert.equal tokens expected ;
ok ()
| _, errors -> Error errors in
assert_tokenize_result "int"
[ (Ident "int") ] >>? fun () ->
assert_tokenize_result "100"
[ (Int "100") ] >>? fun () ->
assert_tokenize_result "(option int)"
[ Open_paren ; Ident "option" ; Ident "int" ; Close_paren ] >>? fun () ->
assert_tokenize_result "DIP { ADD }"
[ Ident "DIP" ; Open_brace ; Ident "ADD" ; Close_brace ] >>? fun () ->
assert_tokenize_result "\"hello\""
[ String "hello" ] >>? fun () ->
assert_tokenize_result "parameter int;"
[ Ident "parameter" ; Ident "int" ; Semi ] >>? fun () ->
assert_tokenize_result "PUSH string \"abcd\";"
[ Ident "PUSH" ; Ident "string" ; String "abcd" ; Semi ] >>? fun () ->
assert_tokenize_result "DROP; SWAP"
[ Ident "DROP" ; Semi ; Ident "SWAP" ] >>? fun () ->
assert_tokenize_result "string"
[ Ident "string" ]
let test_parsing () =
let assert_parses source expected =
let open Micheline_parser in
match tokenize source with
| _, (_ :: _ as errors) -> Error errors
| tokens, [] ->
match Micheline_parser.parse_toplevel tokens with
| _, (_ :: _ as errors) -> Error errors
| ast, [] ->
let ast = List.map Micheline.strip_locations ast in
let expected = List.map Micheline.strip_locations expected in
Assert.equal (List.length ast) (List.length expected) ;
List.iter2 (Assert.equal ~prn) ast expected ;
ok () in
assert_parses "PUSH int 100"
[ (Prim ((), "PUSH", [ Prim ((), "int", [], None) ;
Int ((), "100") ], None)) ] >>? fun () ->
assert_parses "DROP" [ (Prim ((), "DROP", [], None)) ] >>? fun () ->
assert_parses "DIP{DROP}"
[ Prim ((), "DIP", [ Seq((), [ Prim ((), "DROP", [], None) ], None) ], None) ] >>? fun () ->
assert_parses "LAMBDA int int {}"
[ Prim ((), "LAMBDA", [ Prim ((), "int", [], None) ;
Prim ((), "int", [], None) ;
Seq ((), [ ], None) ], None) ] >>? fun () ->
assert_parses "LAMBDA @name int int {}"
[ Prim ((), "LAMBDA", [ Prim ((), "int", [], None) ;
Prim ((), "int", [], None) ;
Seq ((), [ ], None) ], Some "@name") ] >>? fun () ->
assert_parses "NIL @annot string; # comment\n"
[ Prim ((), "NIL", [ Prim ((), "string", [], None) ], Some "@annot") ] >>? fun () ->
assert_parses "PUSH (pair bool string) (Pair False \"abc\")"
[ Prim ((), "PUSH", [ Prim ((), "pair",
[ Prim ((), "bool", [], None) ;
Prim ((), "string", [], None) ], None) ;
Prim ((), "Pair",
[ Prim ((), "False", [], None) ;
String ((), "abc")], None) ], None) ] >>? fun () ->
assert_parses "PUSH (list nat) (List 1 2 3)"
[ Prim ((), "PUSH", [ Prim ((), "list",
[ Prim ((), "nat", [], None) ], None) ;
Prim ((), "List",
[ Int((), "1");
Int ((), "2");
Int ((), "3")],
None) ], None) ] >>? fun () ->
assert_parses "PUSH (lambda nat nat) {}"
[ Prim ((), "PUSH", [ Prim ((), "lambda",
[ Prim ((), "nat", [], None);
Prim ((), "nat", [], None)], None) ;
Seq((), [], None)],
None) ] >>? fun () ->
assert_parses "PUSH key \"tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx\""
[ Prim ((), "PUSH", [ Prim ((), "key", [], None) ;
String ((),"tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx") ],
None) ] >>? fun () ->
assert_parses "PUSH (map int bool) (Map (Item 100 False))"
[ Prim ((), "PUSH", [ Prim ((), "map",
[ Prim((), "int", [], None);
Prim((), "bool", [], None)], None) ;
Prim ((), "Map",
[Prim ((), "Item",
[Int ((), "100");
Prim ((), "False", [], None)], None)], None) ],
None) ] >>? fun () ->
assert_parses
"parameter int; \
return int; \
storage unit; \
code {}"
[ Prim ((), "parameter", [ Prim((), "int", [], None) ], None);
Prim ((), "return", [ Prim((), "int", [], None) ], None);
Prim ((), "storage", [ Prim((), "unit", [], None) ], None);
Prim ((), "code", [ Seq((), [], None) ], None)] >>? fun () ->
assert_parses
"parameter int; \
storage unit; \
return int; \
code {CAR; PUSH int 1; ADD; UNIT; SWAP; PAIR};"
[ Prim ((), "parameter", [ Prim((), "int", [], None) ], None);
Prim ((), "storage", [ Prim((), "unit", [], None) ], None);
Prim ((), "return", [ Prim((), "int", [], None) ], None);
Prim ((), "code", [ Seq((), [ Prim ((), "CAR", [], None) ;
Prim ((), "PUSH", [ Prim((), "int", [], None) ;
Int ((), "1")], None) ;
Prim ((), "ADD", [], None) ;
Prim ((), "UNIT", [], None) ;
Prim ((), "SWAP", [], None) ;
Prim ((), "PAIR", [], None)], None) ], None)] >>? fun () ->
assert_parses
"code {DUP @test; DROP}"
[ Prim ((), "code", [Seq ((), [ Prim ((), "DUP", [], Some "@test");
Prim ((), "DROP", [], None)], None)], None) ] >>? fun () ->
assert_parses
"IF {CAR} {CDR}"
[ Prim ((), "IF", [ Seq ((), [ Prim ((), "CAR", [], None) ], None);
Seq ((), [ Prim ((), "CDR", [], None) ], None) ], None) ] >>? fun () ->
assert_parses
"IF_NONE {FAIL} {}"
[ Prim ((), "IF_NONE", [ Seq ((), [ Prim ((), "FAIL", [], None) ], None);
Seq ((), [ ], None) ], None) ]
let tests = [
"lexing", (fun _ -> Lwt.return (test_lexing ())) ;
"parsing", (fun _ -> Lwt.return (test_parsing ())) ;
"expansion", (fun _ -> Lwt.return (test_expansion ())) ;
"consistency", (fun _ -> Lwt.return (test_unexpansion_consistency ())) ;
"tez-litterals", (fun _ -> test_known_tez_litterals ()) ;
"rnd-tez-litterals", (fun _ -> test_random_tez_litterals ()) ;
]
let () =
Test.run "michelson." tests