From fef4337e8399102a64e04a7ae96cbb6231c647d0 Mon Sep 17 00:00:00 2001 From: Christian Rinderknecht Date: Tue, 26 Mar 2019 10:31:55 +0100 Subject: [PATCH] Support for empty sets and maps. Alternate syntax for maps and lists: sets, maps and lists are now homogeneous. Lists by extension now require the "list" keyword, like sets and maps. Semicolons needed, instead of commas. New syntax for lists: `list [e_1; e_2; ...; e_n]`. The empty list can now be denoted either by `list []` or `list end` or `nil'. Both `list` and `nil` are new keywords. Lists can also be denoted without brackets, as sets and maps: `list e_1; e_2; ...; e_n end`. The extension for maps follows the extension for sets: `map [b_1; b_2; ...; b_n]` or `maps b_1; ...; b_n end`. --- AST.ml | 103 ++++++++++++++++----------- AST.mli | 51 +++++++++----- LexToken.mli | 2 + LexToken.mll | 82 ++++++++++++---------- ParToken.mly | 24 ++++--- Parser.mly | 149 +++++++++++++++++++++++++++++++++------- Tests/crowdfunding.ligo | 12 ++-- 7 files changed, 288 insertions(+), 135 deletions(-) diff --git a/AST.ml b/AST.ml index 134552265..66acbc3cf 100644 --- a/AST.ml +++ b/AST.ml @@ -39,6 +39,7 @@ let sepseq_to_region to_region = function (* Keywords of LIGO *) +type keyword = Region.t type kwd_and = Region.t type kwd_begin = Region.t type kwd_block = Region.t @@ -56,8 +57,10 @@ type kwd_function = Region.t type kwd_if = Region.t type kwd_in = Region.t type kwd_is = Region.t +type kwd_list = Region.t type kwd_map = Region.t type kwd_mod = Region.t +type kwd_nil = Region.t type kwd_not = Region.t type kwd_of = Region.t type kwd_or = Region.t @@ -378,10 +381,10 @@ and map_patch = { } and map_injection = { - opening : kwd_map; - bindings : (binding reg, semi) nsepseq; + opening : opening; + bindings : (binding reg, semi) sepseq; terminator : semi option; - closing : kwd_end + closing : closing } and binding = { @@ -518,12 +521,20 @@ and set_expr = SetInj of set_injection reg and set_injection = { - opening : kwd_set; - elements : (expr, semi) nsepseq; + opening : opening; + elements : (expr, semi) sepseq; terminator : semi option; - closing : kwd_end + closing : closing } +and opening = + Kwd of keyword +| KwdBracket of keyword * lbracket + +and closing = + End of kwd_end +| RBracket of rbracket + and map_expr = MapLookUp of map_lookup reg | MapInj of map_injection reg @@ -581,9 +592,22 @@ and string_expr = | String of Lexer.lexeme reg and list_expr = - Cons of cons bin_op reg -| List of (expr, comma) nsepseq brackets reg -| EmptyList of empty_list reg + Cons of cons bin_op reg +| List of list_injection reg +| Nil of nil par reg + +and list_injection = { + opening : opening; + elements : (expr, semi) sepseq; + terminator : semi option; + closing : closing +} + +and nil = { + nil : kwd_nil; + colon : colon; + list_type : type_expr +} and constr_expr = SomeApp of (c_Some * arguments) reg @@ -615,15 +639,6 @@ and record_projection = { and tuple = (expr, comma) nsepseq par reg -and empty_list = typed_empty_list par - -and typed_empty_list = { - lbracket : lbracket; - rbracket : rbracket; - colon : colon; - list_type : type_expr -} - and none_expr = typed_none_expr par and typed_none_expr = { @@ -725,9 +740,9 @@ and string_expr_to_region = function | String {region; _} -> region and list_expr_to_region = function - Cons {region; _} -| List {region; _} -| EmptyList {region; _} -> region + Cons {region; _} +| List {region; _} +| Nil {region; _} -> region and constr_expr_to_region = function NoneExpr {region; _} @@ -1283,8 +1298,8 @@ and print_string_expr = function and print_list_expr = function Cons {value = {arg1; op; arg2}; _} -> print_expr arg1; print_token op "#"; print_expr arg2 -| List e -> print_list e -| EmptyList e -> print_empty_list e +| List e -> print_list_injection e +| Nil e -> print_nil e and print_constr_expr = function SomeApp e -> print_some_app e @@ -1356,17 +1371,27 @@ and print_set_remove node = and print_map_injection {value; _} = let {opening; bindings; terminator; closing} = value in - print_token opening "map"; - print_nsepseq ";" print_binding bindings; + print_opening "map" opening; + print_sepseq ";" print_binding bindings; print_terminator terminator; - print_token closing "end" + print_closing closing and print_set_injection {value; _} = - let {opening; elements; terminator; closing} = value in - print_token opening "set"; - print_nsepseq ";" print_expr elements; + let {opening; elements; terminator; closing} : set_injection = value in + print_opening "set" opening; + print_sepseq ";" print_expr elements; print_terminator terminator; - print_token closing "end" + print_closing closing + +and print_opening lexeme = function + Kwd kwd -> print_token kwd lexeme +| KwdBracket (kwd, lbracket) -> + print_token kwd lexeme; + print_token lbracket "{" + +and print_closing = function + RBracket rbracket -> print_token rbracket "}" +| End kwd_end -> print_token kwd_end "end" and print_binding {value; _} = let {source; arrow; image} = value in @@ -1380,18 +1405,18 @@ and print_tuple {value; _} = print_nsepseq "," print_expr inside; print_token rpar ")" -and print_list {value; _} = - let {lbracket; inside; rbracket} = value in - print_token lbracket "["; - print_nsepseq "," print_expr inside; - print_token rbracket "]" +and print_list_injection {value; _} = + let {opening; elements; terminator; closing} : list_injection = value in + print_opening "list" opening; + print_sepseq ";" print_expr elements; + print_terminator terminator; + print_closing closing -and print_empty_list {value; _} = +and print_nil {value; _} = let {lpar; inside; rpar} = value in - let {lbracket; rbracket; colon; list_type} = inside in + let {nil; colon; list_type} = inside in print_token lpar "("; - print_token lbracket "["; - print_token rbracket "]"; + print_token nil "nil"; print_token colon ":"; print_type_expr list_type; print_token rpar ")" diff --git a/AST.mli b/AST.mli index c026c0630..3811bc968 100644 --- a/AST.mli +++ b/AST.mli @@ -23,6 +23,7 @@ val sepseq_to_region : ('a -> Region.t) -> ('a,'sep) sepseq -> Region.t (* Keywords of LIGO *) +type keyword = Region.t type kwd_and = Region.t type kwd_begin = Region.t type kwd_block = Region.t @@ -40,8 +41,10 @@ type kwd_function = Region.t type kwd_if = Region.t type kwd_in = Region.t type kwd_is = Region.t +type kwd_list = Region.t type kwd_map = Region.t type kwd_mod = Region.t +type kwd_nil = Region.t type kwd_not = Region.t type kwd_of = Region.t type kwd_or = Region.t @@ -362,10 +365,10 @@ and map_patch = { } and map_injection = { - opening : kwd_map; - bindings : (binding reg, semi) nsepseq; + opening : opening; + bindings : (binding reg, semi) sepseq; terminator : semi option; - closing : kwd_end + closing : closing } and binding = { @@ -502,12 +505,20 @@ and set_expr = SetInj of set_injection reg and set_injection = { - opening : kwd_set; - elements : (expr, semi) nsepseq; + opening : opening; + elements : (expr, semi) sepseq; terminator : semi option; - closing : kwd_end + closing : closing } +and opening = + Kwd of keyword +| KwdBracket of keyword * lbracket + +and closing = + End of kwd_end +| RBracket of rbracket + and map_expr = MapLookUp of map_lookup reg | MapInj of map_injection reg @@ -565,9 +576,22 @@ and string_expr = | String of Lexer.lexeme reg and list_expr = - Cons of cons bin_op reg -| List of (expr, comma) nsepseq brackets reg -| EmptyList of empty_list reg + Cons of cons bin_op reg +| List of list_injection reg +| Nil of nil par reg + +and list_injection = { + opening : opening; + elements : (expr, semi) sepseq; + terminator : semi option; + closing : closing +} + +and nil = { + nil : kwd_nil; + colon : colon; + list_type : type_expr +} and constr_expr = SomeApp of (c_Some * arguments) reg @@ -599,15 +623,6 @@ and record_projection = { and tuple = (expr, comma) nsepseq par reg -and empty_list = typed_empty_list par - -and typed_empty_list = { - lbracket : lbracket; - rbracket : rbracket; - colon : colon; - list_type : type_expr -} - and none_expr = typed_none_expr par and typed_none_expr = { diff --git a/LexToken.mli b/LexToken.mli index e9d8a7f9a..c4934962d 100644 --- a/LexToken.mli +++ b/LexToken.mli @@ -82,8 +82,10 @@ type t = | If of Region.t (* "if" *) | In of Region.t (* "in" *) | Is of Region.t (* "is" *) +| List of Region.t (* "list" *) | Map of Region.t (* "map" *) | Mod of Region.t (* "mod" *) +| Nil of Region.t (* "nil" *) | Not of Region.t (* "not" *) | Of of Region.t (* "of" *) | Or of Region.t (* "or" *) diff --git a/LexToken.mll b/LexToken.mll index bd7dc49ac..7f4eba9b7 100644 --- a/LexToken.mll +++ b/LexToken.mll @@ -81,8 +81,10 @@ type t = | If of Region.t (* "if" *) | In of Region.t (* "in" *) | Is of Region.t (* "is" *) +| List of Region.t (* "list" *) | Map of Region.t (* "map" *) | Mod of Region.t (* "mod" *) +| Nil of Region.t (* "nil" *) | Not of Region.t (* "not" *) | Of of Region.t (* "of" *) | Or of Region.t (* "or" *) @@ -206,8 +208,10 @@ let proj_token = function | If region -> region, "If" | In region -> region, "In" | Is region -> region, "Is" +| List region -> region, "List" | Map region -> region, "Map" | Mod region -> region, "Mod" +| Nil region -> region, "Nil" | Not region -> region, "Not" | Of region -> region, "Of" | Or region -> region, "Or" @@ -286,22 +290,23 @@ let to_lexeme = function | Const _ -> "const" | Contains _ -> "contains" | Down _ -> "down" -| Fail _ -> "fail" -| If _ -> "if" -| In _ -> "in" -| Is _ -> "is" +| Else _ -> "else" +| End _ -> "end" | Entrypoint _ -> "entrypoint" +| Fail _ -> "fail" | For _ -> "for" | From _ -> "from" | Function _ -> "function" -| Type _ -> "type" +| If _ -> "if" +| In _ -> "in" +| Is _ -> "is" +| List _ -> "list" +| Map _ -> "map" +| Mod _ -> "mod" +| Nil _ -> "nil" +| Not _ -> "not" | Of _ -> "of" | Or _ -> "or" -| Var _ -> "var" -| End _ -> "end" -| Then _ -> "then" -| Else _ -> "else" -| Map _ -> "map" | Patch _ -> "patch" | Procedure _ -> "procedure" | Record _ -> "record" @@ -310,9 +315,10 @@ let to_lexeme = function | Skip _ -> "skip" | Step _ -> "step" | Storage _ -> "storage" +| Then _ -> "then" | To _ -> "to" -| Mod _ -> "mod" -| Not _ -> "not" +| Type _ -> "type" +| Var _ -> "var" | While _ -> "while" | With _ -> "with" @@ -346,22 +352,23 @@ let keywords = [ (fun reg -> Const reg); (fun reg -> Contains reg); (fun reg -> Down reg); - (fun reg -> Fail reg); - (fun reg -> If reg); - (fun reg -> In reg); - (fun reg -> Is reg); + (fun reg -> Else reg); + (fun reg -> End reg); (fun reg -> Entrypoint reg); (fun reg -> For reg); (fun reg -> From reg); (fun reg -> Function reg); - (fun reg -> Type reg); + (fun reg -> Fail reg); + (fun reg -> If reg); + (fun reg -> In reg); + (fun reg -> Is reg); + (fun reg -> List reg); + (fun reg -> Map reg); + (fun reg -> Mod reg); + (fun reg -> Nil reg); + (fun reg -> Not reg); (fun reg -> Of reg); (fun reg -> Or reg); - (fun reg -> Var reg); - (fun reg -> End reg); - (fun reg -> Then reg); - (fun reg -> Else reg); - (fun reg -> Map reg); (fun reg -> Patch reg); (fun reg -> Procedure reg); (fun reg -> Record reg); @@ -370,9 +377,10 @@ let keywords = [ (fun reg -> Skip reg); (fun reg -> Step reg); (fun reg -> Storage reg); + (fun reg -> Then reg); (fun reg -> To reg); - (fun reg -> Mod reg); - (fun reg -> Not reg); + (fun reg -> Type reg); + (fun reg -> Var reg); (fun reg -> While reg); (fun reg -> With reg) ] @@ -574,22 +582,23 @@ let is_kwd = function | Const _ | Contains _ | Down _ -| Fail _ -| If _ -| In _ -| Is _ +| Else _ +| End _ | Entrypoint _ +| Fail _ | For _ | From _ | Function _ -| Type _ +| If _ +| In _ +| Is _ +| List _ +| Map _ +| Mod _ +| Nil _ +| Not _ | Of _ | Or _ -| Var _ -| End _ -| Then _ -| Else _ -| Map _ | Patch _ | Procedure _ | Record _ @@ -598,9 +607,10 @@ let is_kwd = function | Skip _ | Step _ | Storage _ +| Then _ | To _ -| Mod _ -| Not _ +| Type _ +| Var _ | While _ | With _ -> true | _ -> false diff --git a/ParToken.mly b/ParToken.mly index ee5697457..dcddbcca7 100644 --- a/ParToken.mly +++ b/ParToken.mly @@ -49,22 +49,23 @@ %token Const (* "const" *) %token Contains (* "contains" *) %token Down (* "down" *) +%token Else (* "else" *) +%token End (* "end" *) +%token Entrypoint (* "entrypoint" *) %token Fail (* "fail" *) +%token For (* "for" *) +%token Function (* "function" *) %token From (* "from" *) %token If (* "if" *) %token In (* "in" *) %token Is (* "is" *) -%token Entrypoint (* "entrypoint" *) -%token For (* "for" *) -%token Function (* "function" *) -%token Type (* "type" *) +%token List (* "list" *) +%token Map (* "map" *) +%token Mod (* "mod" *) +%token Nil (* "nil" *) +%token Not (* "not" *) %token Of (* "of" *) %token Or (* "or" *) -%token Var (* "var" *) -%token End (* "end" *) -%token Then (* "then" *) -%token Else (* "else" *) -%token Map (* "map" *) %token Patch (* "patch" *) %token Procedure (* "procedure" *) %token Record (* "record" *) @@ -73,9 +74,10 @@ %token Skip (* "skip" *) %token Step (* "step" *) %token Storage (* "storage" *) +%token Then (* "then" *) %token To (* "to" *) -%token Mod (* "mod" *) -%token Not (* "not" *) +%token Type (* "type" *) +%token Var (* "var" *) %token While (* "while" *) %token With (* "with" *) diff --git a/Parser.mly b/Parser.mly index 2365810b5..42ab41617 100644 --- a/Parser.mly +++ b/Parser.mly @@ -177,6 +177,13 @@ core_type: let tuple = {region; value={lpar; inside=inside,[]; rpar}} in TApp {region=total; value = type_constr, tuple} } +| List par(type_expr) { + let total = cover $1 $2.region in + let type_constr = {value="list"; region=$1} in + let {region; value = {lpar; inside; rpar}} = $2 in + let tuple = {region; value={lpar; inside=inside,[]; rpar}} + in TApp {region=total; value = type_constr, tuple} + } | par(type_expr) { TPar $1 } @@ -390,18 +397,17 @@ unqualified_decl(OP): let init = match $5.value with `Expr e -> e - | `EList (lbracket, rbracket) -> + | `EList kwd_nil -> let region = $5.region and value = { - lbracket; - rbracket; + nil = kwd_nil; colon = Region.ghost; list_type = $3} in let value = { lpar = Region.ghost; inside = value; rpar = Region.ghost} in - EList (EmptyList {region; value}) + EList (Nil {region; value}) | `ENone region -> let value = { lpar = Region.ghost; @@ -451,13 +457,12 @@ var_decl: } extended_expr: - expr { {region = expr_to_region $1; - value = `Expr $1} } -| LBRACKET RBRACKET { {region = cover $1 $2; - value = `EList ($1,$2)} } -| C_None { {region = $1; value = `ENone $1} } -| map_injection { {region = $1.region; value = `EMap $1} } -| set_injection { {region = $1.region; value = `ESet $1} } + expr { {region = expr_to_region $1; + value = `Expr $1} } +| Nil { {region = $1; value = `EList $1} } +| C_None { {region = $1; value = `ENone $1} } +| map_injection { {region = $1.region; value = `EMap $1} } +| set_injection { {region = $1.region; value = `ESet $1} } instruction: single_instr { Single $1 } @@ -527,11 +532,39 @@ set_injection: Set series(expr,End) { let first, (others, terminator, closing) = $2 in let region = cover $1 closing - and value = { - opening = $1; - elements = first, others; + and value : set_injection = { + opening = Kwd $1; + elements = Some (first, others); terminator; - closing} + closing = End closing} + in {region; value} + } +| Set End { + let region = cover $1 $2 + and value : set_injection = { + opening = Kwd $1; + elements = None; + terminator = None; + closing = End $2} + in {region; value} + } +| Set LBRACKET series(expr,RBRACKET) { + let first, (others, terminator, closing) = $3 in + let region = cover $1 closing + and value : set_injection = { + opening = KwdBracket ($1,$2); + elements = Some (first, others); + terminator; + closing = RBracket closing} + in {region; value} + } +| Set LBRACKET RBRACKET { + let region = cover $1 $3 + and value : set_injection = { + opening = KwdBracket ($1,$2); + elements = None; + terminator = None; + closing = RBracket $3} in {region; value} } @@ -540,10 +573,38 @@ map_injection: let first, (others, terminator, closing) = $2 in let region = cover $1 closing and value = { - opening = $1; - bindings = first, others; + opening = Kwd $1; + bindings = Some (first, others); terminator; - closing} + closing = End closing} + in {region; value} + } +| Map End { + let region = cover $1 $2 + and value = { + opening = Kwd $1; + bindings = None; + terminator = None; + closing = End $2} + in {region; value} + } +| Map LBRACKET series(binding,RBRACKET) { + let first, (others, terminator, closing) = $3 in + let region = cover $1 closing + and value = { + opening = KwdBracket ($1,$2); + bindings = Some (first, others); + terminator; + closing = RBracket closing} + in {region; value} + } +| Map LBRACKET RBRACKET { + let region = cover $1 $3 + and value = { + opening = KwdBracket ($1,$2); + bindings = None; + terminator = None; + closing = RBracket $3} in {region; value} } @@ -873,7 +934,7 @@ core_expr: | C_Unit { EUnit $1 } | tuple { ETuple $1 } | list_expr { EList (List $1) } -| empty_list { EList (EmptyList $1) } +| nil { EList (Nil $1) } | none_expr { EConstr (NoneExpr $1) } | fun_call { ECall $1 } | map_expr { EMap $1 } @@ -953,17 +1014,53 @@ arguments: tuple { $1 } list_expr: - brackets(nsepseq(expr,COMMA)) { $1 } + List series(expr,End) { + let first, (others, terminator, closing) = $2 in + let region = cover $1 closing + and value : list_injection = { + opening = Kwd $1; + elements = Some (first, others); + terminator; + closing = End closing} + in {region; value} + } +| List End { + let region = cover $1 $2 + and value : list_injection = { + opening = Kwd $1; + elements = None; + terminator = None; + closing = End $2} + in {region; value} + } +| List LBRACKET series(expr,RBRACKET) { + let first, (others, terminator, closing) = $3 in + let region = cover $1 closing + and value : list_injection = { + opening = KwdBracket ($1,$2); + elements = Some (first, others); + terminator; + closing = RBracket closing} + in {region; value} + } +| List LBRACKET RBRACKET { + let region = cover $1 $3 + and value : list_injection = { + opening = KwdBracket ($1,$2); + elements = None; + terminator = None; + closing = RBracket $3} + in {region; value} + } -empty_list: +nil: par(typed_empty_list) { $1 } typed_empty_list: - LBRACKET RBRACKET COLON type_expr { - {lbracket = $1; - rbracket = $2; - colon = $3; - list_type = $4} + Nil COLON type_expr { + {nil = $1; + colon = $2; + list_type = $3} } none_expr: diff --git a/Tests/crowdfunding.ligo b/Tests/crowdfunding.ligo index ec6119096..58f230fbc 100644 --- a/Tests/crowdfunding.ligo +++ b/Tests/crowdfunding.ligo @@ -10,7 +10,9 @@ entrypoint contribute (storage store : store; const sender : address; const amount : mutez) : store * list (operation) is - var operations : list (operation) := [] + var operations : list (operation) := nil + const s : list (int) = list [1; 2; 3] + const t : set (int) = set [] block { if now > store.deadline then fail "Deadline passed"; @@ -24,14 +26,14 @@ entrypoint contribute (storage store : store; entrypoint withdraw (storage store : store; const sender : address) : store * list (operation) is - var operations : list (operation) := [] + var operations : list (operation) := list end begin if sender = owner then if now (Unit) >= store.deadline then if balance >= store.goal then { store.funded := True; // patch store with record funded = True end; - operations := [Transfer (owner, balance)]; + operations := list [Transfer (owner, balance)]; }; else fail "Below target" else { fail "Too soon"; } @@ -40,7 +42,7 @@ entrypoint withdraw (storage store : store; const sender : address) entrypoint claim (storage store : store; const sender : address) : store * list (operation) is - var operations : list (operation) := [] + var operations : list (operation) := list [] var amount : mutez := 0 begin if now <= store.deadline then @@ -54,7 +56,7 @@ entrypoint claim (storage store : store; const sender : address) fail "Cannot refund" else begin - operations := [Transfer (sender, amount)]; + operations := list [Transfer (sender, amount)]; remove sender from map store.backers end end