(* * SPDX-FileCopyrightText: 2024 The Forester Project Contributors * * SPDX-License-Identifier: GPL-3.0-or-later *) %{ open Forester_prelude open Forester_core %} %token XML_ELT_IDENT %token DECL_XMLNS %token TEXT VERBATIM %token COMMENT %token WHITESPACE %token IDENT %token HASH_IDENT %token IMPORT EXPORT DEF NAMESPACE LET FUN OPEN %token OBJECT PATCH CALL %token SUBTREE SCOPE PUT GET DEFAULT ALLOC %token SLASH LBRACE RBRACE LSQUARE RSQUARE LPAREN RPAREN HASH_LBRACE HASH_HASH_LBRACE TICK AT_SIGN HASH %token EOF %token DATALOG %token DX_ENTAILED %token DX_VAR %start main %% let locate(p) == | x = p; { Asai.Range.locate_lex $loc x } let braces(p) == delimited(LBRACE, p, RBRACE) let squares(p) == delimited(LSQUARE, p, RSQUARE) let parens(p) == delimited(LPAREN, p, RPAREN) let bvar := | x = TEXT; { x } let bvar_with_strictness := | x = TEXT; { match String_util.explode x with | '~' :: chars -> Lazy, String_util.implode chars | _ -> Strict, x } let binder == list(squares(bvar_with_strictness)) let ws_or(p) := | WHITESPACE; { [] } | x = p; { [x] } let ws_list(p) := flatten(list(ws_or(p))) let separated_ws_list(sep,p) := flatten(separated_list(sep,ws_or(p))) let textual_node := | ~ = TEXT; | ~ = WHITESPACE; | ~ = head_node1; let code_expr == ws_list(locate(head_node1)) let textual_expr == list(locate(textual_node)) let patch_bindings := | self = squares(bvar); super = squares(bvar); { Some self, Some super } | self = squares(bvar); { Some self, None } | { None, None } let head_node := | DEF; (~,~,~) = fun_spec; | ALLOC; ~ = ident; | EXPORT; ~ = txt_arg; | NAMESPACE; ~ = ident; ~ = braces(code_expr); | SUBTREE; ~ = option(squares(wstext)); ~ = braces(ws_list(locate(head_node))); | FUN; ~ = binder; ~ = arg; | LET; (~,~,~) = fun_spec; | ~ = ident; | ~ = HASH_IDENT; | SCOPE; ~ = arg; | PUT; ~ = ident; ~ = arg; | DEFAULT; ~ = ident; ~ = arg; | GET; ~ = ident; | OPEN; ~ = ident; | (~,~) = XML_ELT_IDENT; | ~ = DECL_XMLNS; ~ = txt_arg; | OBJECT; self = option(squares(bvar)); methods = braces(ws_list(method_decl)); { Code.Object {self; methods } } | PATCH; obj = braces(code_expr); (self, super) = patch_bindings; methods = braces(ws_list(method_decl)); { Code.Patch {obj; self; super; methods} } | CALL; ~ = braces(code_expr); ~ = txt_arg; | DATALOG; LBRACE; list(WHITESPACE); ~ = dx_sequent_node; RBRACE; <> | ~ = VERBATIM; | ~ = delimited(HASH_LBRACE, textual_expr, RBRACE); | ~ = delimited(HASH_HASH_LBRACE, textual_expr, RBRACE); | ~ = braces(textual_expr); | ~ = squares(textual_expr); | ~ = parens(textual_expr); | ~ = COMMENT; let head_node1 := | ~ = head_node; <> | DX_ENTAILED; { Code.Text "-:" } | x = DX_VAR; { Code.Text ("?" ^ x) } | TICK; { Code.Text "'" } | AT_SIGN; { Code.Text "@" } | HASH; { Code.Text "#" } let method_decl := | k = squares(TEXT); list(WHITESPACE); v = arg; { k, v } let ident := | ~ = separated_nonempty_list(SLASH, IDENT); <> let ws_or_text := | x = TEXT; { x } | x = WHITESPACE; { x } let wstext := | xs = list(ws_or_text); { String.concat "" xs } let arg := | braces(textual_expr) | located_str = locate(VERBATIM); { [{located_str with value = Code.Verbatim located_str.value}] } let txt_arg == braces(wstext) let fun_spec == ~ = ident; ~ = binder; ~ = arg; <> let head_node_or_import := | head_node | IMPORT; ~ = txt_arg; let main := | ~ = ws_list(locate(head_node_or_import)); EOF; <> let dx_rel := | x = locate(ident); { [Range.{x with value = Code.Ident x.value}] } | TICK; ~ = arg; <> let dx_term_node := | ~ = DX_VAR; | TICK; ~ = arg; | AT_SIGN; ~ = arg; let dx_term := | x = locate(dx_term_node); { [x] } let dx_prop_node := | ~ = dx_rel; ~ = ws_list(dx_term); let dx_prop := | p = locate(dx_prop_node); { [p] } let dx_sequent_node := | ~ = dx_prop; DX_ENTAILED; ~ = ws_list(braces(dx_prop)); | x = DX_VAR; list(WHITESPACE); DX_ENTAILED; pos = ws_list(braces(dx_prop)); { Code.Dx_query (x, pos, []) } | ~ = DX_VAR; list(WHITESPACE); DX_ENTAILED; ~ = ws_list(braces(dx_prop)); HASH; ~ = ws_list(braces(dx_prop));