From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: =?UTF-8?Q?Augustin_Ch=C3=A9neau_=28BTuin=29?= Newsgroups: gmane.emacs.devel Subject: Re: New tree-sitter mode: bison-ts-mode Date: Sat, 23 Sep 2023 01:21:31 +0200 Message-ID: <95a5f464-a669-44f2-b25d-0ce1017dd505@mailo.com> References: <871qeqwtbk.fsf@posteo.net> <97f90014-6507-44ac-a61c-4e74d058063f@mailo.com> <87zg1e7xh7.fsf@posteo.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------Lq0ToYflO0LiYFVdltsp7jo1" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="14719"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mozilla Thunderbird Cc: emacs-devel To: Philip Kaludercic Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sat Sep 23 01:22:39 2023 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qjpTr-0003de-06 for ged-emacs-devel@m.gmane-mx.org; Sat, 23 Sep 2023 01:22:39 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qjpSy-0000YO-DG; Fri, 22 Sep 2023 19:21:44 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qjpSw-0000YF-Po for emacs-devel@gnu.org; Fri, 22 Sep 2023 19:21:43 -0400 Original-Received: from msg-1.mailo.com ([213.182.54.11]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qjpSs-0006wD-6l for emacs-devel@gnu.org; Fri, 22 Sep 2023 19:21:42 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=mailo.com; s=mailo; t=1695424891; bh=s2yIVYN2+k5OEAQ9sv1R1wj101S77zx05xNXtNwKsHs=; h=X-EA-Auth:Content-Type:Message-ID:Date:MIME-Version:From:Subject: To:References:Cc:In-Reply-To; b=G3xEOb5M/wHK4h/9G/0TrOdvlwbtH5JSbTLPVgXa5JS07pYXi9msUzqgF+YNIX6zw 5qFB/lYVSFc69CQnVGFeaMDqVoRSRgPQgXuYtf24clZdRtMuQTl4tl7dTeR1um7L1j dYELqILZ3M4ARb3sCxSXhVn6xCqwBzC/7opfztlk= Original-Received: by b221-6.in.mailobj.net [192.168.90.26] with ESMTP via ip-20.mailobj.net [213.182.54.20] Sat, 23 Sep 2023 01:21:31 +0200 (CEST) X-EA-Auth: RNgphJejeQVjdqmIuDXuQv01P+jwwInnxse0Us6gDAc0TsHJ2KfD60ie6XBGp3hiDtPVZtQTuSyIxTu9XUBeWrVey1/WugyU Content-Language: fr, en-US In-Reply-To: <87zg1e7xh7.fsf@posteo.net> Received-SPF: pass client-ip=213.182.54.11; envelope-from=btuin@mailo.com; helo=msg-1.mailo.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:310990 Archived-At: This is a multi-part message in MIME format. --------------Lq0ToYflO0LiYFVdltsp7jo1 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Le 22/09/2023 à 22:40, Philip Kaludercic a écrit : > "Augustin Chéneau (BTuin)" writes: > >> Le 22/09/2023 à 09:38, Philip Kaludercic a écrit : >>> "Augustin Chéneau (BTuin)" writes: >>> A few comments on the proposed file: >>> >>>> ;;; bison-ts-mode --- Tree-sitter mode for Bison -*- lexical-binding: t; -*- >>>> >>> Could you add the usual header information here? >>> ;; Copyright (C) 2022-2023 Free Software Foundation, Inc. >>> --8<---------------cut here---------------start------------->8--- >>> ;; Author: Augustin Chéneau >>> ;; This file is part of GNU Emacs. >>> ;; GNU Emacs is free software: you can redistribute it and/or modify >>> ;; it under the terms of the GNU General Public License as published by >>> ;; the Free Software Foundation, either version 3 of the License, or >>> ;; (at your option) any later version. >>> ;; GNU Emacs is distributed in the hope that it will be useful, >>> ;; but WITHOUT ANY WARRANTY; without even the implied warranty of >>> ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >>> ;; GNU General Public License for more details. >>> ;; You should have received a copy of the GNU General Public License >>> ;; along with GNU Emacs. If not, see . >>> --8<---------------cut here---------------end--------------->8--- >>> >>>> ;;; Commentary: >>>> >>>> ;; This is a mode based on tree-sitter for Bison and Yacc files, tools to generate parsers. >>> Shouldn't you mention what tree sitter grammar is being being used >>> here? >>> >>>> ;;; Code: >>>> >>>> (require 'treesit) >>>> (require 'c-ts-common) >>>> >>>> (declare-function treesit-parser-create "treesit.c") >>>> (declare-function treesit-induce-sparse-tree "treesit.c") >>>> (declare-function treesit-node-child-by-field-name "treesit.c") >>>> (declare-function treesit-search-subtree "treesit.c") >>>> (declare-function treesit-node-parent "treesit.c") >>>> (declare-function treesit-node-next-sibling "treesit.c") >>>> (declare-function treesit-node-type "treesit.c") >>>> (declare-function treesit-node-child "treesit.c") >>>> (declare-function treesit-node-end "treesit.c") >>>> (declare-function treesit-node-start "treesit.c") >>>> (declare-function treesit-node-string "treesit.c") >>>> (declare-function treesit-query-compile "treesit.c") >>>> (declare-function treesit-query-capture "treesit.c") >>>> (declare-function treesit-parser-add-notifier "treesit.c") >>>> (declare-function treesit-parser-buffer "treesit.c") >>>> (declare-function treesit-parser-list "treesit.c") >>>> >>>> >>>> (defgroup bison nil >>> bison or bison-ts? >>> >> >> As far as I know, no group is explicitly reserved for >> tree-sitter. rust-ts-mode uses the group "rust", java-ts-mode uses >> "java", ruby-ts-mode uses "ruby"... > > OK, that is fine then. > >> >>>> "Support for Bison and Yacc." >>> Shouldn't tree-sitter be mentioned here? >>> >> Same as above. > > Sure? Above I was talking about using the group name "bison" or > "bison-ts", this is about mentioning that tree sitter is required for > the mode to work. > Oh ok, I modified it. Should I detail more? >>>> :group 'languages) >>>> >>>> (defcustom bison-ts-mode-indent-offset 2 >>>> "Number of spaces for each indentation step in `bison-ts-mode'. >>>> It has no effect in the epilogue part of the file." >>>> :version "30.1" >>>> :type 'integer >>>> :safe 'integerp >>>> :group 'bison) >>>> >>>> (defcustom bison-ts-mode-autodetect-language t >>>> "Search for a %language directive in the file at initialization. >>>> Changing the value of this directive in the file requires to reload the mode to >>>> be effective. If `bison-ts-mode-buffer-language' is set by a file-local >>>> variable, the auto-detection is not run." >>>> :version "30.1" >>>> :type 'boolean >>>> :safe 'boolean >>>> :group 'bison) >>>> >>>> (defvar-local bison-ts-mode-embedded-language nil >>>> "Embedded language in Bison buffer.") >>>> >>>> (defun bison-ts-mode--merge-feature-lists (l1 l2) >>>> "Merge the lists of lists L1 and L2. >>>> The first sublist of L1 is merged with the first sublist of L2 and so on. >>>> L1 and L2 don't need to have the same size." >>>> (let ((res ())) >>>> (while (or l1 l2) >>>> (setq res (push (append (car l1) (car l2)) res)) >>>> (setq l1 (cdr l1) l2 (cdr l2))) >>>> (nreverse res))) >>> Is this a generic function that should be extracted into some common >>> file? >>> >> It probably should, it could be useful for other *-ts-mode that want >> to mix multiple languages, unless there already is such a function in >> Elisp. > > Then again, if you use `cl-loop', you can reproduce this specific > behaviour using > > (cl-loop for i1 in '(1 2 3) collect i1 > for i2 in '(a b c) collect i2) > Maybe I'm missing something, but this is not the wanted result. The goal is to merge to lists of lists, such as '((1 2 3) (4 5 6) (7 8 9)) and '((a b c) (d e f) (h g i) (k l m)), to get '((1 2 3 a b c) (4 5 6 d e f) (7 8 9 h g i) (k l m)) (the order inside the sub-lists is unimportant, '(1 c b 2 3 a) would work too). >> >>>> (defun bison-ts-mode--find-language-in-buffer (&optional buffer) >>>> "Find and return the language set by the Bison directive %language. >>>> If BUFFER is set, search in this buffer, otherwise search in the current >>>> buffer." >>>> (save-excursion >>>> (when buffer >>>> (switch-to-buffer buffer)) >>> Or rather? >>> (with-current-buffer (or buffer (current-buffer)) >>> (save-excursion >>> ...)) >>> >>>> (goto-char (point-min)) >>>> (let ((pos-end >>>> (re-search-forward >>>> (rx >>>> bol (0+ blank) "%language" (0+ blank) "\"" (1+ (in alpha "+")) "\"") >>>> nil >>>> t)) >>>> (pos-beg nil)) >>>> (when pos-end >>> Using when-let might be nice here. >>> >>>> (goto-char (1- pos-end)) >>>> (setq pos-beg (1+ (search-backward "\"" nil t))) >>>> (buffer-substring-no-properties pos-beg (1- pos-end)))))) >>> I'd use a single regular expression here with a group, then extract >>> the >>> right information using `match-string'. >>> >> Nice, I didn't know it was possible. >>>> >>>> >>>> (defun bison-ts-mode--detect-language (&optional buffer) >>>> "Dectect the embedded language in a Bison buffer. >>>> Known languages are C, C++, D, and Java, but D is not supported as there is >>>> no support for tree-sitter D in Emacs yet. >>>> If BUFFER is set, search in this buffer, otherwise search in the current >>>> buffer." >>>> (if-let ((str (bison-ts-mode--find-language-in-buffer buffer))) >>>> (pcase (downcase str) >>>> ("c" 'c) >>>> ("c++" 'cpp) >>>> ("d" (progn (message "D language not yet supported") nil)) >>> Each pcase case has an implicit progn. >>> >>>> ("java" 'java)) >>>> (progn >>> Use when-let to avoid this progn. >>> >>>> (message >>>> "bison-ts-mode: %%language specification not found or invalid, defaulting to C.") >>> Is it necessary to prefix the message with the major mode name? >>> >> If feared it would be a bit cryptic without. Anyway I modified this >> function to only display a message if "%language" is present but the >> language is invalid, so it probably isn't necessary anymore. > > OK. > >>>> 'c))) >>>> >>>> >>>> (defun bison-ts-mode--language-at-point-function (position) >>>> "Return the language at POSITION." >>>> (let* ((node (treesit-node-at position 'bison))) >>> ^ >>> let is enough >>> >>>> (if (equal (treesit-node-type node) >>>> "embedded_code") >>> There is no need to break the line here. >>> >>>> bison-ts-mode-embedded-language >>>> 'bison))) >>>> >>>> (defun bison-ts-mode--font-lock-settings (language) >>>> "Return the font-lock settings for Bison. >>>> LANGUAGE should be set to \\='bison." >>>> (treesit-font-lock-rules >>>> :language language >>>> :feature 'bison-comment >>>> '((comment) @font-lock-comment-face) >>>> >>>> :language language >>>> :feature 'bison-declaration >>>> '((declaration_name) @font-lock-keyword-face) >>>> >>>> :language language >>>> :feature 'bison-type >>>> '((type) @font-lock-type-face) >>>> >>>> :language language >>>> :feature 'bison-grammar-rule-usage >>>> '((grammar_rule_identifier) @font-lock-variable-use-face) >>>> >>>> :language language >>>> :feature 'bison-grammar-rule-declaration >>>> '((grammar_rule (grammar_rule_declaration) >>>> @font-lock-variable-use-face)) >>>> >>>> :language language >>>> :feature 'bison-string >>>> :override t >>>> '((string) @font-lock-string-face) >>>> >>>> :language language >>>> :feature 'bison-literal >>>> :override t >>>> '((char_literal) @font-lock-keyword-face >>>> (number_literal) @font-lock-number-face) >>>> >>>> :language language >>>> :feature 'bison-directive-grammar-rule >>>> :override t >>>> '((grammar_rule (directive) @font-lock-keyword-face)) >>>> >>>> :language language >>>> :feature 'bison-operator >>>> :override t >>>> '(["|"] @font-lock-operator-face) >>>> >>>> :language language >>>> :feature 'bison-delimiter >>>> :override t >>>> '([";"] @font-lock-delimiter-face))) >>>> >>>> >>>> (defvar bison-ts-mode--font-lock-feature-list >>>> '(( bison-comment bison-declaration bison-type >>>> bison-grammar-rule-usage bison-grammar-rule-declaration >>>> bison-string bison-literal bison-directive-grammar-rule >>>> bison-operator bison-delimiter))) >>>> >>>> >>>> (defun bison-ts-mode--bison-matcher-action (root-name) >>>> "Treesit matcher to check if NODE at BOL is not located in the epilogue. >>>> ROOT-NAME is the highest-level node of the embedded language." >>>> (lambda (node _parent bol &rest _) >>>> (if (equal (treesit-node-type (treesit-node-parent node)) root-name) >>>> (let* ((bison-node (treesit-node-at bol 'bison))) >>> ^ >>> here again, let is enough >>> (if (equal >>>> (treesit-node-type >>>> (treesit-node-parent(treesit-node-parent bison-node))) "action") >>> Though you could bind the (treesit-node-type ...) expression under >>> the >>> above let. >>> >>>> t >>>> nil))))) >>> Why (if foo t nil) when foo would do the same job (equal only >>> returns >>> nil and t, so normalising the value isn't even necessary). >>> >> Because I was stupid. > > OK, I am glad I didn't miss something obvious ^^ > >>>> >>>> (defun bison-ts-mode--bison-matcher-not-epilogue (root-name) >>>> "Treesit matcher to check if NODE at BOL is not located in the epilogue. >>>> ROOT-NAME is the highest-level node of the embedded language." >>>> (lambda (node _parent bol &rest _) >>>> (if (equal (treesit-node-type (treesit-node-parent node)) root-name) >>>> (let* ((bison-node (treesit-node-at bol 'bison))) >>>> (if (equal (treesit-node-type (treesit-node-parent bison-node)) "epilogue") >>>> nil >>>> t))))) >>> Am I missing something, or couldn't these two functions be merged if >>> you >>> give them a third argument NODE-TYPE and pass it "action" or "epilogue". >>> >> No, bison-ts-mode--bison-matcher-action checks if the _grandparent_ is >> an "action" node, while bison-ts-mode--bison-matcher-not-epilogue >> checks if the _parent_ is an "epilogue" node. > > In that case, would adding another parameter and then binding the > returned lambda expression via defalias be worth the effort? > I'm not sure, everything inside the let body is different, any ressemblance is coincidental. Doing so would probably add more complexity than it would remove. >>>> >>>> >>>> (defun bison-ts-mode--bison-parent (_node _parent bol &rest _) >>>> "Get the parent of the bison node at BOL." >>>> (treesit-node-start (treesit-node-parent (treesit-node-at bol 'bison)))) >>>> >>>> >>>> (defun bison-ts-mode--indent-rules () >>>> "Indent rules supported by `bison-ts-mode'." >>>> (let* >>>> ((common >>>> `(((node-is "^declaration$") >>>> column-0 0) >>>> ((and (parent-is "^declaration$") >>>> (not (node-is "^code_block$"))) >>>> column-0 2) >>>> ((and (parent-is "comment") c-ts-common-looking-at-star) >>>> c-ts-common-comment-start-after-first-star -1) >>>> (c-ts-common-comment-2nd-line-matcher >>>> c-ts-common-comment-2nd-line-anchor >>>> 1) >>>> ((parent-is "comment") prev-adaptive-prefix 0) >>>> >>>> ;; Opening and closing brackets "{}" of declarations >>>> ((and (parent-is "^declaration$") >>>> (node-is "^code_block$")) >>>> column-0 0) >>>> ((and (n-p-gp "}" "" "^declaration$")) >>>> column-0 0) >>>> ((parent-is "^declaration$") parent 2) >>>> ((node-is "^grammar_rule$") column-0 0) >>>> ((and >>>> (parent-is "^grammar_rule$") >>>> (node-is ";")) >>>> column-0 bison-ts-mode-indent-offset) >>>> ((and (parent-is "^grammar_rule$") >>>> (node-is "|")) >>>> column-0 bison-ts-mode-indent-offset) >>>> ((and (parent-is "^grammar_rule$") >>>> (not (node-is "^grammar_rule_declaration$")) >>>> (not (node-is "^action$"))) >>>> column-0 ,(+ bison-ts-mode-indent-offset 2)) >>>> ((or >>>> (node-is "^action$") >>>> (node-is "^}$")) >>>> column-0 12) >>>> ;; Set '%%' at the beginning of the line >>>> ((or >>>> (and (parent-is "^grammar_rules_section$") >>>> (node-is "%%")) >>>> (node-is "^grammar_rules_section$")) >>>> column-0 0) >>>> (no-node parent-bol 0)))) >>>> `((bison . ,common) >>>> ;; Import and override embedded languages rules to add an offset >>>> ,(pcase bison-ts-mode-embedded-language >>>> ('c `(c >>>> ((bison-ts-mode--bison-matcher-action "translation_unit") >>>> bison-ts-mode--bison-parent ,bison-ts-mode-indent-offset) >>>> ((bison-ts-mode--bison-matcher-not-epilogue "translation_unit") >>>> column-0 ,bison-ts-mode-indent-offset) >>>> ,@(alist-get 'c (c-ts-mode--get-indent-style 'c)))) >>>> ('cpp `(cpp >>>> ((bison-ts-mode--bison-matcher-action "translation_unit") >>>> bison-ts-mode--bison-parent ,bison-ts-mode-indent-offset) >>>> ((bison-ts-mode--bison-matcher-not-epilogue "translation_unit") >>>> parent-0 ,bison-ts-mode-indent-offset) >>>> ,@(alist-get 'cpp (c-ts-mode--get-indent-style 'cpp)))) >>>> ('java `(java >>>> ((bison-ts-mode--bison-matcher-action "program") >>>> bison-ts-mode--bison-parent ,bison-ts-mode-indent-offset) >>>> ((bison-ts-mode--bison-matcher-not-epilogue "program") >>>> column-0 ,bison-ts-mode-indent-offset) >>>> ,@java-ts-mode--indent-rules)))))) >>>> >>>> >>>> (define-derived-mode bison-ts-mode prog-mode "Bison" >>>> "A mode for Bison." >>> ^ >>> major-mode >>> Also, mentioning tree-sitter seems like something worth doing. >>> >>>> (when (treesit-ready-p 'bison) >>>> (when (not bison-ts-mode-embedded-language) >>>> (setq bison-ts-mode-embedded-language (bison-ts-mode--detect-language))) >>>> >>>> ;; Require only if needed, to avoid warnings if a grammar is not >>>> ;; installed but not used. >>>> (pcase bison-ts-mode-embedded-language >>> Would a `pcase-exhaustive' be appropriate here? >>> >> No, the language D is recognized but not supported yet in Emacs, so if >> this language is detected it will not configure anything. > > There is a d-mode in NonGNU ELPA, but I guess that isn't enough since > you need a d-ts-mode, right? > Yes, a d-ts-mode is what's needed here, because it needs tree-sitter rules about indentation and font-locking. >>>> ('c (require 'c-ts-mode)) >>>> ('cpp (require 'c-ts-mode)) >>>> ('java (require 'java-ts-mode))) >>>> >>>> (setq-local treesit-font-lock-settings >>>> (append (bison-ts-mode--font-lock-settings 'bison) >>>> (pcase bison-ts-mode-embedded-language >>>> ('c (c-ts-mode--font-lock-settings 'c)) >>>> ('cpp (c-ts-mode--font-lock-settings 'cpp)) >>>> ('java java-ts-mode--font-lock-settings)))) >>>> >>>> (setq-local treesit-font-lock-feature-list >>>> (if bison-ts-mode-embedded-language >>>> (bison-ts-mode--merge-feature-lists >>>> bison-ts-mode--font-lock-feature-list >>>> (pcase bison-ts-mode-embedded-language >>>> ('c c-ts-mode--feature-list) >>>> ('cpp c-ts-mode--feature-list) >>>> ('java java-ts-mode--feature-list))) >>>> bison-ts-mode--font-lock-feature-list)) >>>> >>>> (setq-local treesit-simple-imenu-settings >>>> `(("Grammar" >>>> "\\`grammar_rule_declaration\\'" >>>> nil >>>> (lambda (node) (substring-no-properties (treesit-node-text node)))))) >>>> >>>> (c-ts-common-comment-setup) >>>> >>>> (setq-local treesit-simple-indent-rules >>>> (bison-ts-mode--indent-rules)) >>>> >>>> (setq-local treesit-language-at-point-function 'bison-ts-mode--language-at-point-function) >>>> >>>> (when bison-ts-mode-embedded-language >>>> (setq-local treesit-range-settings >>>> (treesit-range-rules >>>> :embed bison-ts-mode-embedded-language >>>> :host 'bison >>>> :local t >>>> '((embedded_code) @capture)))) >>>> >>>> (treesit-major-mode-setup))) >>>> >>>> (provide 'bison-ts-mode) >>>> ;;; bison-ts-mode.el ends here >>> Sorry for the number of comments, but there has been a discussion on >>> the >>> code-quality of tree-sitter major modes that has been less than optimal, >>> so I hope that your contribution could help raise the bar. >> >> No problem, thank you for your review! > > [...] > >> >> (defgroup bison-ts nil >> "Support for Bison and Yacc." >> :group 'languages) >> >> (defcustom bison-ts-mode-indent-offset 2 >> "Number of spaces for each indentation step in `bison-ts-mode'. >> It has no effect in the epilogue part of the file." >> :version "30.1" >> :type 'integer >> :safe 'integerp >> :group 'bison) > > The ":group" annotations here are not necessary in general, defcustoms > can automatically detect the previous defgroup. > >> (defcustom bison-ts-mode-autodetect-language t >> "Search for a %language directive in the file at initialization. >> Changing the value of this directive in the file requires to reload the mode to >> be effective. If `bison-ts-mode-buffer-language' is set by a file-local >> variable, the auto-detection is not run." >> :version "30.1" >> :type 'boolean >> :safe 'boolean >> :group 'bison) >> >> (defvar-local bison-ts-mode-embedded-language nil >> "Embedded language in Bison buffer. >> Supported values are `c', `cpp', and `java'.") >> ;;;###autoload >> (put 'bison-ts-mode-embedded-language 'safe-local-variable 'symbolp) >> >> >> (defun bison-ts-mode--merge-feature-lists (l1 l2) >> "Merge the lists of lists L1 and L2. >> The first sublist of L1 is merged with the first sublist of L2 and so on. >> L1 and L2 don't need to have the same size." >> (let ((res ())) >> (while (or l1 l2) >> (setq res (push (seq-uniq (append (car l1) (car l2)) 'eq) res)) >> (setq l1 (cdr l1) l2 (cdr l2))) >> (nreverse res))) >> >> (defun bison-ts-mode--find-language-in-buffer (&optional buffer) >> "Find and return the language set by the Bison directive %language. >> If BUFFER is set, search in this buffer, otherwise search in the current >> buffer." >> (save-excursion >> (with-current-buffer (or buffer (current-buffer)) >> (goto-char (point-min)) >> (when >> (re-search-forward >> (rx >> bol (0+ blank) "%language" (0+ blank) "\"" (group (1+ (in alpha "+"))) "\"") > > I'd say this regular expression is complex enough to be split into > multiple lines. And you can use the fact that `rx' takes s-expressions > to add comments inbetween. > >> nil >> t))) >> (substring-no-properties (match-string 1)))) > > Or `match-string-no-properties' > >> >> >> (defun bison-ts-mode--detect-language (&optional buffer) >> "Dectect the embedded language in a Bison buffer. >> Known languages are C, C++, D, and Java, but D is not supported as there is >> no support for tree-sitter D in Emacs yet. >> If BUFFER is set, search in this buffer, otherwise search in the current >> buffer." >> (if-let ((str (bison-ts-mode--find-language-in-buffer buffer))) >> (pcase-exhaustive (downcase str) >> ("c" 'c) >> ("c++" 'cpp) >> ("d" (message "D language not yet supported") nil) >> ("java" 'java) >> (_ (message "%%language specification \"%s\" is invalid, defaulting to C" str) 'c)))) > > No point in using `pcase-exhaustive' if you end with _ anyway? > >> >> >> (defun bison-ts-mode--language-at-point-function (position) >> "Return the language at POSITION." >> (let ((node (treesit-node-at position 'bison))) >> (if (equal (treesit-node-type node) "embedded_code") >> bison-ts-mode-embedded-language >> 'bison))) >> >> (defun bison-ts-mode--font-lock-settings (language) >> "Return the font-lock settings for Bison. >> LANGUAGE should be set to \\='bison." >> (treesit-font-lock-rules >> :language language >> :feature 'comment >> '((comment) @font-lock-comment-face) >> >> :language language >> :feature 'declaration >> '((declaration_name) @font-lock-keyword-face) >> >> :language language >> :feature 'type >> '((type) @font-lock-type-face) >> >> :language language >> :feature 'variable >> '((grammar_rule_identifier) @font-lock-variable-use-face) >> >> :language language >> :feature 'grammar-declaration >> '((grammar_rule (grammar_rule_declaration) >> @font-lock-variable-use-face)) >> >> :language language >> :feature 'string >> :override t >> '((string) @font-lock-string-face) >> >> :language language >> :feature 'literal >> :override t >> '((char_literal) @font-lock-keyword-face >> (number_literal) @font-lock-number-face) >> >> :language language >> :feature 'directive-grammar-rule >> :override t >> '((grammar_rule (directive) @font-lock-keyword-face)) >> >> :language language >> :feature 'operator >> :override t >> '(["|"] @font-lock-operator-face) >> >> :language language >> :feature 'delimiter >> :override t >> '([";"] @font-lock-delimiter-face))) >> >> >> (defvar bison-ts-mode--font-lock-feature-list > > I am not that familiar with the tree-sitter stuff, but would it be > possible to use `defconst' here? > I made if defconst, but I'm not sure. Maybe some people would want to modify it? >> '(( comment declaration grammar-declaration) >> ( type string directive-grammar-rule) >> ( literal) >> ( variable operator delimiter))) >> >> >> (defun bison-ts-mode--bison-matcher-action (root-name) >> "Treesit matcher to check if NODE at BOL is located in an action node. >> ROOT-NAME is the highest-level node of the embedded language." >> (lambda (node _parent bol &rest _) >> (if (equal (treesit-node-type (treesit-node-parent node)) root-name) >> (let ((bison-node (treesit-node-at bol 'bison))) >> (equal >> (treesit-node-type >> (treesit-node-parent (treesit-node-parent bison-node))) >> "action"))))) >> >> (defun bison-ts-mode--bison-matcher-not-epilogue (root-name) >> "Treesit matcher to check if NODE at BOL is not located in the epilogue. >> ROOT-NAME is the highest-level node of the embedded language." >> (lambda (node _parent bol &rest _) >> (if (equal (treesit-node-type (treesit-node-parent node)) root-name) >> (let ((bison-node (treesit-node-at bol 'bison))) >> (not (equal (treesit-node-type (treesit-node-parent bison-node)) "epilogue")))))) >> >> >> (defun bison-ts-mode--bison-parent (_node _parent bol &rest _) >> "Get the parent of the bison node at BOL." >> (treesit-node-start (treesit-node-parent (treesit-node-at bol 'bison)))) >> >> >> (defun bison-ts-mode--indent-rules () >> "Indent rules supported by `bison-ts-mode'." >> (let* >> ((common >> `(((node-is "^declaration$") >> column-0 0) >> ((and (parent-is "^declaration$") >> (not (node-is "^code_block$"))) >> column-0 2) >> ((and (parent-is "comment") c-ts-common-looking-at-star) >> c-ts-common-comment-start-after-first-star -1) >> (c-ts-common-comment-2nd-line-matcher >> c-ts-common-comment-2nd-line-anchor >> 1) >> ((parent-is "comment") prev-adaptive-prefix 0) >> >> ;; Opening and closing brackets "{}" of declarations >> ((and (parent-is "^declaration$") >> (node-is "^code_block$")) >> column-0 0) >> ((and (n-p-gp "}" "" "^declaration$")) >> column-0 0) >> ((parent-is "^declaration$") parent 2) >> ((node-is "^grammar_rule$") column-0 0) >> ((and >> (parent-is "^grammar_rule$") >> (node-is ";")) >> column-0 bison-ts-mode-indent-offset) >> ((and (parent-is "^grammar_rule$") >> (node-is "|")) >> column-0 bison-ts-mode-indent-offset) >> ((and (parent-is "^grammar_rule$") >> (not (node-is "^grammar_rule_declaration$")) >> (not (node-is "^action$"))) >> column-0 ,(+ bison-ts-mode-indent-offset 2)) >> ((or >> (node-is "^action$") >> (node-is "^}$")) >> column-0 12) >> ;; Set '%%' at the beginning of the line >> ((or >> (and (parent-is "^grammar_rules_section$") >> (node-is "%%")) >> (node-is "^grammar_rules_section$")) >> column-0 0) >> (no-node parent-bol 0)))) >> `((bison . ,common) >> ;; Import and override embedded languages rules to add an offset >> ,(pcase bison-ts-mode-embedded-language >> ('c `(c >> ((bison-ts-mode--bison-matcher-action "translation_unit") >> bison-ts-mode--bison-parent ,bison-ts-mode-indent-offset) >> ((bison-ts-mode--bison-matcher-not-epilogue "translation_unit") >> column-0 ,bison-ts-mode-indent-offset) >> ,@(alist-get 'c (c-ts-mode--get-indent-style 'c)))) >> ('cpp `(cpp >> ((bison-ts-mode--bison-matcher-action "translation_unit") >> bison-ts-mode--bison-parent ,bison-ts-mode-indent-offset) >> ((bison-ts-mode--bison-matcher-not-epilogue "translation_unit") >> parent-0 ,bison-ts-mode-indent-offset) >> ,@(alist-get 'cpp (c-ts-mode--get-indent-style 'cpp)))) >> ('java `(java >> ((bison-ts-mode--bison-matcher-action "program") >> bison-ts-mode--bison-parent ,bison-ts-mode-indent-offset) >> ((bison-ts-mode--bison-matcher-not-epilogue "program") >> column-0 ,bison-ts-mode-indent-offset) >> ,@java-ts-mode--indent-rules)))))) >> >> >> (define-derived-mode bison-ts-mode prog-mode "Bison" >> "A major-mode for Bison based on tree-sitter." >> (when (treesit-ready-p 'bison) >> (when (not bison-ts-mode-embedded-language) > > Or `unless' > >> (setq bison-ts-mode-embedded-language (bison-ts-mode--detect-language))) >> >> ;; Require only if needed, to avoid warnings if a grammar is not >> ;; installed but not used. >> (pcase bison-ts-mode-embedded-language >> ('c (require 'c-ts-mode)) >> ('cpp (require 'c-ts-mode)) >> ('java (require 'java-ts-mode))) >> >> (setq-local treesit-font-lock-settings >> (append (bison-ts-mode--font-lock-settings 'bison) >> (pcase bison-ts-mode-embedded-language >> ('c (c-ts-mode--font-lock-settings 'c)) >> ('cpp (c-ts-mode--font-lock-settings 'cpp)) >> ('java java-ts-mode--font-lock-settings)))) >> >> (setq-local treesit-font-lock-feature-list >> (if bison-ts-mode-embedded-language >> (bison-ts-mode--merge-feature-lists >> bison-ts-mode--font-lock-feature-list >> (pcase bison-ts-mode-embedded-language >> ('c c-ts-mode--feature-list) >> ('cpp c-ts-mode--feature-list) >> ('java java-ts-mode--feature-list))) >> bison-ts-mode--font-lock-feature-list)) >> >> (setq-local treesit-simple-imenu-settings >> `(("Grammar" >> "\\`grammar_rule_declaration\\'" >> nil >> (lambda (node) (substring-no-properties (treesit-node-text node)))))) > > The function `treesit-node-text' appears to take an optional NO-PROPERTY argument. > >> >> (c-ts-common-comment-setup) >> >> (setq-local treesit-simple-indent-rules >> (bison-ts-mode--indent-rules)) >> >> (setq-local treesit-language-at-point-function 'bison-ts-mode--language-at-point-function) >> >> (when bison-ts-mode-embedded-language >> (setq-local treesit-range-settings >> (treesit-range-rules >> :embed bison-ts-mode-embedded-language >> :host 'bison >> :local t >> '((embedded_code) @capture)))) >> >> (treesit-major-mode-setup))) >> >> (provide 'bison-ts-mode) >> ;;; bison-ts-mode.el ends here I've also fixed a few issues and replaced some "if" by "when". Sorry, you'll receive this message twice as I misclicked and only sent the previous message to you... (I also fixed an issue in the meantime) --------------Lq0ToYflO0LiYFVdltsp7jo1 Content-Type: text/x-emacs-lisp; charset=UTF-8; name="bison-ts-mode.el" Content-Disposition: attachment; filename="bison-ts-mode.el" Content-Transfer-Encoding: base64 Ozs7IGJpc29uLXRzLW1vZGUgLS0tIHRyZWUtc2l0dGVyIHN1cHBvcnQgZm9yIEJpc29uIC0q LSBsZXhpY2FsLWJpbmRpbmc6IHQ7IC0qLQoKOzsgQ29weXJpZ2h0IChDKSAyMDIzIEZyZWUg U29mdHdhcmUgRm91bmRhdGlvbiwgSW5jLgoKOzsgQXV0aG9yICAgOiBBdWd1c3RpbiBDaMOp bmVhdSA8YnR1aW5AbWFpbG8uY29tPgo7OyBLZXl3b3JkcyA6IGJpc29uIHlhY2MgbGFuZ3Vh Z2VzIHRyZWUtc2l0dGVyCgo7OyBUaGlzIGZpbGUgaXMgcGFydCBvZiBHTlUgRW1hY3MuCgo7 OyBHTlUgRW1hY3MgaXMgZnJlZSBzb2Z0d2FyZTogeW91IGNhbiByZWRpc3RyaWJ1dGUgaXQg YW5kL29yIG1vZGlmeQo7OyBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFs IFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieQo7OyB0aGUgRnJlZSBTb2Z0d2FyZSBG b3VuZGF0aW9uLCBlaXRoZXIgdmVyc2lvbiAzIG9mIHRoZSBMaWNlbnNlLCBvcgo7OyAoYXQg eW91ciBvcHRpb24pIGFueSBsYXRlciB2ZXJzaW9uLgoKOzsgR05VIEVtYWNzIGlzIGRpc3Ry aWJ1dGVkIGluIHRoZSBob3BlIHRoYXQgaXQgd2lsbCBiZSB1c2VmdWwsCjs7IGJ1dCBXSVRI T1VUIEFOWSBXQVJSQU5UWTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9m Cjs7IE1FUkNIQU5UQUJJTElUWSBvciBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9T RS4gIFNlZSB0aGUKOzsgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yIG1vcmUgZGV0 YWlscy4KCjs7IFlvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBH ZW5lcmFsIFB1YmxpYyBMaWNlbnNlCjs7IGFsb25nIHdpdGggR05VIEVtYWNzLiAgSWYgbm90 LCBzZWUgPGh0dHBzOi8vd3d3LmdudS5vcmcvbGljZW5zZXMvPi4KCjs7OyBDb21tZW50YXJ5 OgoKOzsgVGhpcyBpcyBhIG1vZGUgYmFzZWQgb24gdHJlZS1zaXR0ZXIgZm9yIEJpc29uIGFu ZCBZYWNjIGZpbGVzLCB0b29scyB0bwo7OyBnZW5lcmF0ZSBwYXJzZXJzLiAgVGhlIGdyYW1t YXIgdXNlZCBpcyBhdmFpbGFibGUgaGVyZToKOzsgaHR0cHM6Ly9naXRsYWIuY29tL2J0dWlu Mi90cmVlLXNpdHRlci1iaXNvbgoKOzs7IENvZGU6CgoocmVxdWlyZSAndHJlZXNpdCkKKHJl cXVpcmUgJ2MtdHMtY29tbW9uKQoKKGRlY2xhcmUtZnVuY3Rpb24gdHJlZXNpdC1wYXJzZXIt Y3JlYXRlICJ0cmVlc2l0LmMiKQooZGVjbGFyZS1mdW5jdGlvbiB0cmVlc2l0LWluZHVjZS1z cGFyc2UtdHJlZSAidHJlZXNpdC5jIikKKGRlY2xhcmUtZnVuY3Rpb24gdHJlZXNpdC1ub2Rl LWNoaWxkLWJ5LWZpZWxkLW5hbWUgInRyZWVzaXQuYyIpCihkZWNsYXJlLWZ1bmN0aW9uIHRy ZWVzaXQtc2VhcmNoLXN1YnRyZWUgInRyZWVzaXQuYyIpCihkZWNsYXJlLWZ1bmN0aW9uIHRy ZWVzaXQtbm9kZS1wYXJlbnQgInRyZWVzaXQuYyIpCihkZWNsYXJlLWZ1bmN0aW9uIHRyZWVz aXQtbm9kZS1uZXh0LXNpYmxpbmcgInRyZWVzaXQuYyIpCihkZWNsYXJlLWZ1bmN0aW9uIHRy ZWVzaXQtbm9kZS10eXBlICJ0cmVlc2l0LmMiKQooZGVjbGFyZS1mdW5jdGlvbiB0cmVlc2l0 LW5vZGUtY2hpbGQgInRyZWVzaXQuYyIpCihkZWNsYXJlLWZ1bmN0aW9uIHRyZWVzaXQtbm9k ZS1lbmQgInRyZWVzaXQuYyIpCihkZWNsYXJlLWZ1bmN0aW9uIHRyZWVzaXQtbm9kZS1zdGFy dCAidHJlZXNpdC5jIikKKGRlY2xhcmUtZnVuY3Rpb24gdHJlZXNpdC1ub2RlLXN0cmluZyAi dHJlZXNpdC5jIikKKGRlY2xhcmUtZnVuY3Rpb24gdHJlZXNpdC1xdWVyeS1jb21waWxlICJ0 cmVlc2l0LmMiKQooZGVjbGFyZS1mdW5jdGlvbiB0cmVlc2l0LXF1ZXJ5LWNhcHR1cmUgInRy ZWVzaXQuYyIpCihkZWNsYXJlLWZ1bmN0aW9uIHRyZWVzaXQtcGFyc2VyLWFkZC1ub3RpZmll ciAidHJlZXNpdC5jIikKKGRlY2xhcmUtZnVuY3Rpb24gdHJlZXNpdC1wYXJzZXItYnVmZmVy ICJ0cmVlc2l0LmMiKQooZGVjbGFyZS1mdW5jdGlvbiB0cmVlc2l0LXBhcnNlci1saXN0ICJ0 cmVlc2l0LmMiKQoKCihkZWZncm91cCBiaXNvbiBuaWwKICAiU3VwcG9ydCBmb3IgQmlzb24g YW5kIFlhY2MgdXNpbmcgdHJlZS1zaXR0ZXIuIgogIDpncm91cCAnbGFuZ3VhZ2VzKQoKKGRl ZmN1c3RvbSBiaXNvbi10cy1tb2RlLWluZGVudC1vZmZzZXQgMgogICJOdW1iZXIgb2Ygc3Bh Y2VzIGZvciBlYWNoIGluZGVudGF0aW9uIHN0ZXAgaW4gYGJpc29uLXRzLW1vZGUnLgpJdCBo YXMgbm8gZWZmZWN0IGluIHRoZSBlcGlsb2d1ZSBwYXJ0IG9mIHRoZSBmaWxlLiIKICA6dmVy c2lvbiAiMzAuMSIKICA6dHlwZSAnaW50ZWdlcgogIDpzYWZlICdpbnRlZ2VycCkKCihkZWZj dXN0b20gYmlzb24tdHMtbW9kZS1hdXRvZGV0ZWN0LWxhbmd1YWdlIHQKICAiU2VhcmNoIGZv ciBhICVsYW5ndWFnZSBkaXJlY3RpdmUgaW4gdGhlIGZpbGUgYXQgaW5pdGlhbGl6YXRpb24u CkNoYW5naW5nIHRoZSB2YWx1ZSBvZiB0aGlzIGRpcmVjdGl2ZSBpbiB0aGUgZmlsZSByZXF1 aXJlcyB0byByZWxvYWQgdGhlIG1vZGUgdG8KYmUgZWZmZWN0aXZlLiAgSWYgYGJpc29uLXRz LW1vZGUtYnVmZmVyLWxhbmd1YWdlJyBpcyBzZXQgYnkgYSBmaWxlLWxvY2FsCiB2YXJpYWJs ZSwgdGhlIGF1dG8tZGV0ZWN0aW9uIGlzIG5vdCBydW4uIgogIDp2ZXJzaW9uICIzMC4xIgog IDp0eXBlICdib29sZWFuCiAgOnNhZmUgJ2Jvb2xlYW4pCgooZGVmdmFyLWxvY2FsIGJpc29u LXRzLW1vZGUtZW1iZWRkZWQtbGFuZ3VhZ2UgbmlsCiAgIkVtYmVkZGVkIGxhbmd1YWdlIGlu IEJpc29uIGJ1ZmZlci4KU3VwcG9ydGVkIHZhbHVlcyBhcmUgYGMnLCBgY3BwJywgYW5kIGBq YXZhJy4iKQo7OzsjIyNhdXRvbG9hZAoocHV0ICdiaXNvbi10cy1tb2RlLWVtYmVkZGVkLWxh bmd1YWdlICdzYWZlLWxvY2FsLXZhcmlhYmxlICdzeW1ib2xwKQoKCihkZWZ1biBiaXNvbi10 cy1tb2RlLS1tZXJnZS1mZWF0dXJlLWxpc3RzIChsMSBsMikKICAiTWVyZ2UgdGhlIGxpc3Rz IG9mIGxpc3RzIEwxIGFuZCBMMi4KVGhlIGZpcnN0IHN1Ymxpc3Qgb2YgTDEgaXMgbWVyZ2Vk IHdpdGggdGhlIGZpcnN0IHN1Ymxpc3Qgb2YgTDIgYW5kIHNvIG9uLgpMMSBhbmQgTDIgZG9u J3QgbmVlZCB0byBoYXZlIHRoZSBzYW1lIHNpemUuIgogIChsZXQgKChyZXMgKCkpKQogICAg KHdoaWxlIChvciBsMSBsMikKICAgICAgKHNldHEgcmVzIChwdXNoIChzZXEtdW5pcSAoYXBw ZW5kIChjYXIgbDEpIChjYXIgbDIpKSAnZXEpIHJlcykpCiAgICAgIChzZXRxIGwxIChjZHIg bDEpIGwyIChjZHIgbDIpKSkKICAgIChucmV2ZXJzZSByZXMpKSkKCihkZWZ1biBiaXNvbi10 cy1tb2RlLS1maW5kLWxhbmd1YWdlLWluLWJ1ZmZlciAoJm9wdGlvbmFsIGJ1ZmZlcikKICAi RmluZCBhbmQgcmV0dXJuIHRoZSBsYW5ndWFnZSBzZXQgYnkgdGhlIEJpc29uIGRpcmVjdGl2 ZSAlbGFuZ3VhZ2UuCklmIEJVRkZFUiBpcyBzZXQsIHNlYXJjaCBpbiB0aGlzIGJ1ZmZlciwg b3RoZXJ3aXNlIHNlYXJjaCBpbiB0aGUgY3VycmVudApidWZmZXIuIgogIChzYXZlLWV4Y3Vy c2lvbgogICAgKHdpdGgtY3VycmVudC1idWZmZXIgKG9yIGJ1ZmZlciAoY3VycmVudC1idWZm ZXIpKQogICAgICAoZ290by1jaGFyIChwb2ludC1taW4pKQogICAgICAod2hlbgogICAgICAg ICAgKHJlLXNlYXJjaC1mb3J3YXJkCiAgICAgICAgICAgKHJ4CiAgICAgICAgICAgIDs7IE9u bHkgY2hlY2sgZm9yIGEgbGFuZ3VhZ2UgZGlyZWN0aXZlIGF0IHRoZSBiZWdpbm5pbmcgb2Yg YSBsaW5lCiAgICAgICAgICAgIDs7IEknbSBub3QgZW50aXJlbHkgc3VyZSBpdCBpcyBhY3R1 YWxseSBtYW5kYXRvcnksIGJ1dCBpbiBwcmFjdGljZQogICAgICAgICAgICA7OyBkaXJlY3Rp dmVzIGFyZSBhbHdheXMgb24gYSBuZXcgbGluZS4KICAgICAgICAgICAgYm9sCiAgICAgICAg ICAgICgwKyBibGFuaykKICAgICAgICAgICAgIiVsYW5ndWFnZSIKICAgICAgICAgICAgKDAr IGJsYW5rKQogICAgICAgICAgICAiXCIiCiAgICAgICAgICAgIDs7IEJpc29uIHN1cHBvcnRz IEMsIEMrKywgSmF2YSwgYW5kIEQuICBUaGV5IGNhbiBiZSBjYXBpdGFsaXplZCBvciBub3Qu CiAgICAgICAgICAgIChncm91cCAoMSsgKGluIGFscGhhICIrIikpKQogICAgICAgICAgICAi XCIiKQogICAgICAgICAgIG5pbAogICAgICAgICAgIHQpKSkKICAgIChtYXRjaC1zdHJpbmct bm8tcHJvcGVydGllcyAxKSkpCgoKKGRlZnVuIGJpc29uLXRzLW1vZGUtLWRldGVjdC1sYW5n dWFnZSAoJm9wdGlvbmFsIGJ1ZmZlcikKICAiRGVjdGVjdCB0aGUgZW1iZWRkZWQgbGFuZ3Vh Z2UgaW4gYSBCaXNvbiBidWZmZXIuCktub3duIGxhbmd1YWdlcyBhcmUgQywgQysrLCBELCBh bmQgSmF2YSwgYnV0IEQgaXMgbm90IHN1cHBvcnRlZCBhcyB0aGVyZSBpcwpubyBzdXBwb3J0 IGZvciB0cmVlLXNpdHRlciBEIGluIEVtYWNzIHlldC4KSWYgQlVGRkVSIGlzIHNldCwgc2Vh cmNoIGluIHRoaXMgYnVmZmVyLCBvdGhlcndpc2Ugc2VhcmNoIGluIHRoZSBjdXJyZW50CmJ1 ZmZlci4iCiAgKGlmLWxldCAoKHN0ciAoYmlzb24tdHMtbW9kZS0tZmluZC1sYW5ndWFnZS1p bi1idWZmZXIgYnVmZmVyKSkpCiAgICAgIChwY2FzZSAoZG93bmNhc2Ugc3RyKQogICAgICAg ICgiYyIgJ2MpCiAgICAgICAgKCJjKysiICdjcHApCiAgICAgICAgKCJkIiAobWVzc2FnZSAi RCBsYW5ndWFnZSBub3QgeWV0IHN1cHBvcnRlZCIpIG5pbCkKICAgICAgICAoImphdmEiICdq YXZhKQogICAgICAgIChfIChtZXNzYWdlICIlJWxhbmd1YWdlIHNwZWNpZmljYXRpb24gXCIl c1wiIGlzIGludmFsaWQsIGRlZmF1bHRpbmcgdG8gQyIgc3RyKSAnYykpKSkKCgooZGVmdW4g Ymlzb24tdHMtbW9kZS0tbGFuZ3VhZ2UtYXQtcG9pbnQtZnVuY3Rpb24gKHBvc2l0aW9uKQog ICJSZXR1cm4gdGhlIGxhbmd1YWdlIGF0IFBPU0lUSU9OLiIKICAobGV0ICgobm9kZSAodHJl ZXNpdC1ub2RlLWF0IHBvc2l0aW9uICdiaXNvbikpKQogICAgKGlmIChlcXVhbCAodHJlZXNp dC1ub2RlLXR5cGUgbm9kZSkgImVtYmVkZGVkX2NvZGUiKQogICAgICAgIGJpc29uLXRzLW1v ZGUtZW1iZWRkZWQtbGFuZ3VhZ2UKICAgICAgJ2Jpc29uKSkpCgooZGVmdW4gYmlzb24tdHMt bW9kZS0tZm9udC1sb2NrLXNldHRpbmdzIChsYW5ndWFnZSkKICAiUmV0dXJuIHRoZSBmb250 LWxvY2sgc2V0dGluZ3MgZm9yIEJpc29uLgpMQU5HVUFHRSBzaG91bGQgYmUgc2V0IHRvIFxc PSdiaXNvbi4iCiAgKHRyZWVzaXQtZm9udC1sb2NrLXJ1bGVzCiAgIDpsYW5ndWFnZSBsYW5n dWFnZQogICA6ZmVhdHVyZSAnY29tbWVudAogICAnKChjb21tZW50KSBAZm9udC1sb2NrLWNv bW1lbnQtZmFjZSkKCiAgIDpsYW5ndWFnZSBsYW5ndWFnZQogICA6ZmVhdHVyZSAnZGVjbGFy YXRpb24KICAgJygoZGVjbGFyYXRpb25fbmFtZSkgQGZvbnQtbG9jay1rZXl3b3JkLWZhY2Up CgogICA6bGFuZ3VhZ2UgbGFuZ3VhZ2UKICAgOmZlYXR1cmUgJ3R5cGUKICAgJygodHlwZSkg QGZvbnQtbG9jay10eXBlLWZhY2UpCgogICA6bGFuZ3VhZ2UgbGFuZ3VhZ2UKICAgOmZlYXR1 cmUgJ3ZhcmlhYmxlCiAgICcoKGdyYW1tYXJfcnVsZV9pZGVudGlmaWVyKSBAZm9udC1sb2Nr LXZhcmlhYmxlLXVzZS1mYWNlKQoKICAgOmxhbmd1YWdlIGxhbmd1YWdlCiAgIDpmZWF0dXJl ICdncmFtbWFyLWRlY2xhcmF0aW9uCiAgICcoKGdyYW1tYXJfcnVsZSAoZ3JhbW1hcl9ydWxl X2RlY2xhcmF0aW9uKQogICAgICAgICAgICAgICAgICAgQGZvbnQtbG9jay12YXJpYWJsZS11 c2UtZmFjZSkpCgogICA6bGFuZ3VhZ2UgbGFuZ3VhZ2UKICAgOmZlYXR1cmUgJ3N0cmluZwog ICA6b3ZlcnJpZGUgdAogICAnKChzdHJpbmcpIEBmb250LWxvY2stc3RyaW5nLWZhY2UpCgog ICA6bGFuZ3VhZ2UgbGFuZ3VhZ2UKICAgOmZlYXR1cmUgJ2xpdGVyYWwKICAgOm92ZXJyaWRl IHQKICAgJygoY2hhcl9saXRlcmFsKSBAZm9udC1sb2NrLWtleXdvcmQtZmFjZQogICAgIChu dW1iZXJfbGl0ZXJhbCkgQGZvbnQtbG9jay1udW1iZXItZmFjZSkKCiAgIDpsYW5ndWFnZSBs YW5ndWFnZQogICA6ZmVhdHVyZSAnZGlyZWN0aXZlLWdyYW1tYXItcnVsZQogICA6b3ZlcnJp ZGUgdAogICAnKChncmFtbWFyX3J1bGUgKGRpcmVjdGl2ZSkgQGZvbnQtbG9jay1rZXl3b3Jk LWZhY2UpKQoKICAgOmxhbmd1YWdlIGxhbmd1YWdlCiAgIDpmZWF0dXJlICdvcGVyYXRvcgog ICA6b3ZlcnJpZGUgdAogICAnKFsifCJdIEBmb250LWxvY2stb3BlcmF0b3ItZmFjZSkKCiAg IDpsYW5ndWFnZSBsYW5ndWFnZQogICA6ZmVhdHVyZSAnZGVsaW1pdGVyCiAgIDpvdmVycmlk ZSB0CiAgICcoWyI7Il0gQGZvbnQtbG9jay1kZWxpbWl0ZXItZmFjZSkpKQoKCihkZWZjb25z dCBiaXNvbi10cy1tb2RlLS1mb250LWxvY2stZmVhdHVyZS1saXN0CiAgJygoIGNvbW1lbnQg ZGVjbGFyYXRpb24gZ3JhbW1hci1kZWNsYXJhdGlvbikKICAgICggdHlwZSBzdHJpbmcgZGly ZWN0aXZlLWdyYW1tYXItcnVsZSkKICAgICggbGl0ZXJhbCkKICAgICggdmFyaWFibGUgb3Bl cmF0b3IgZGVsaW1pdGVyKSkpCgoKKGRlZnVuIGJpc29uLXRzLW1vZGUtLWJpc29uLW1hdGNo ZXItYWN0aW9uIChyb290LW5hbWUpCiAgIlRyZWVzaXQgbWF0Y2hlciB0byBjaGVjayBpZiBO T0RFIGF0IEJPTCBpcyBsb2NhdGVkIGluIGFuIGFjdGlvbiBub2RlLgpST09ULU5BTUUgaXMg dGhlIGhpZ2hlc3QtbGV2ZWwgbm9kZSBvZiB0aGUgZW1iZWRkZWQgbGFuZ3VhZ2UuIgogIChs YW1iZGEgKG5vZGUgX3BhcmVudCBib2wgJnJlc3QgXykKICAgICh3aGVuIChlcXVhbCAodHJl ZXNpdC1ub2RlLXR5cGUgKHRyZWVzaXQtbm9kZS1wYXJlbnQgbm9kZSkpIHJvb3QtbmFtZSkK ICAgICAgKGxldCAoKGJpc29uLW5vZGUgKHRyZWVzaXQtbm9kZS1hdCBib2wgJ2Jpc29uKSkp CiAgICAgICAgKGVxdWFsCiAgICAgICAgICh0cmVlc2l0LW5vZGUtdHlwZQogICAgICAgICAg KHRyZWVzaXQtbm9kZS1wYXJlbnQgKHRyZWVzaXQtbm9kZS1wYXJlbnQgYmlzb24tbm9kZSkp KQogICAgICAgICAiYWN0aW9uIikpKSkpCgooZGVmdW4gYmlzb24tdHMtbW9kZS0tYmlzb24t bWF0Y2hlci1ub3QtZXBpbG9ndWUgKHJvb3QtbmFtZSkKICAiVHJlZXNpdCBtYXRjaGVyIHRv IGNoZWNrIGlmIE5PREUgYXQgQk9MIGlzIG5vdCBsb2NhdGVkIGluIHRoZSBlcGlsb2d1ZS4K Uk9PVC1OQU1FIGlzIHRoZSBoaWdoZXN0LWxldmVsIG5vZGUgb2YgdGhlIGVtYmVkZGVkIGxh bmd1YWdlLiIKICAobGFtYmRhIChub2RlIF9wYXJlbnQgYm9sICZyZXN0IF8pCiAgICAod2hl biAoZXF1YWwgKHRyZWVzaXQtbm9kZS10eXBlICh0cmVlc2l0LW5vZGUtcGFyZW50IG5vZGUp KSByb290LW5hbWUpCiAgICAgIChsZXQgKChiaXNvbi1ub2RlICh0cmVlc2l0LW5vZGUtYXQg Ym9sICdiaXNvbikpKQogICAgICAgIChub3QgKGVxdWFsICh0cmVlc2l0LW5vZGUtdHlwZSAo dHJlZXNpdC1ub2RlLXBhcmVudCBiaXNvbi1ub2RlKSkgImVwaWxvZ3VlIikpKSkpKQoKCihk ZWZ1biBiaXNvbi10cy1tb2RlLS1iaXNvbi1wYXJlbnQgKF9ub2RlIF9wYXJlbnQgYm9sICZy ZXN0IF8pCiAgIkdldCB0aGUgcGFyZW50IG9mIHRoZSBiaXNvbiBub2RlIGF0IEJPTC4iCiAg KHRyZWVzaXQtbm9kZS1zdGFydCAodHJlZXNpdC1ub2RlLXBhcmVudCAodHJlZXNpdC1ub2Rl LWF0IGJvbCAnYmlzb24pKSkpCgoKKGRlZnVuIGJpc29uLXRzLW1vZGUtLWluZGVudC1ydWxl cyAoKQogICJJbmRlbnQgcnVsZXMgc3VwcG9ydGVkIGJ5IGBiaXNvbi10cy1tb2RlJy4iCiAg KGxldCoKICAgICAgKChjb21tb24KICAgICAgICBgKCgobm9kZS1pcyAiXmRlY2xhcmF0aW9u JCIpCiAgICAgICAgICAgY29sdW1uLTAgMCkKICAgICAgICAgICgoYW5kIChwYXJlbnQtaXMg Il5kZWNsYXJhdGlvbiQiKQogICAgICAgICAgICAgICAgKG5vdCAobm9kZS1pcyAiXmNvZGVf YmxvY2skIikpKQogICAgICAgICAgIGNvbHVtbi0wIDIpCiAgICAgICAgICAoKGFuZCAocGFy ZW50LWlzICJjb21tZW50IikgYy10cy1jb21tb24tbG9va2luZy1hdC1zdGFyKQogICAgICAg ICAgIGMtdHMtY29tbW9uLWNvbW1lbnQtc3RhcnQtYWZ0ZXItZmlyc3Qtc3RhciAtMSkKICAg ICAgICAgIChjLXRzLWNvbW1vbi1jb21tZW50LTJuZC1saW5lLW1hdGNoZXIKICAgICAgICAg ICBjLXRzLWNvbW1vbi1jb21tZW50LTJuZC1saW5lLWFuY2hvcgogICAgICAgICAgIDEpCiAg ICAgICAgICAoKHBhcmVudC1pcyAiY29tbWVudCIpIHByZXYtYWRhcHRpdmUtcHJlZml4IDAp CgogICAgICAgICAgOzsgT3BlbmluZyBhbmQgY2xvc2luZyBicmFja2V0cyAie30iIG9mIGRl Y2xhcmF0aW9ucwogICAgICAgICAgKChhbmQgKHBhcmVudC1pcyAiXmRlY2xhcmF0aW9uJCIp CiAgICAgICAgICAgICAgICAobm9kZS1pcyAiXmNvZGVfYmxvY2skIikpCiAgICAgICAgICAg Y29sdW1uLTAgMCkKICAgICAgICAgICgoYW5kIChuLXAtZ3AgIn0iICIiICJeZGVjbGFyYXRp b24kIikpCiAgICAgICAgICAgY29sdW1uLTAgMCkKICAgICAgICAgICgocGFyZW50LWlzICJe ZGVjbGFyYXRpb24kIikgcGFyZW50IDIpCiAgICAgICAgICAoKG5vZGUtaXMgIl5ncmFtbWFy X3J1bGUkIikgY29sdW1uLTAgMCkKICAgICAgICAgICgoYW5kCiAgICAgICAgICAgIChwYXJl bnQtaXMgIl5ncmFtbWFyX3J1bGUkIikKICAgICAgICAgICAgKG5vZGUtaXMgIjsiKSkKICAg ICAgICAgICBjb2x1bW4tMCBiaXNvbi10cy1tb2RlLWluZGVudC1vZmZzZXQpCiAgICAgICAg ICAoKGFuZCAocGFyZW50LWlzICJeZ3JhbW1hcl9ydWxlJCIpCiAgICAgICAgICAgICAgICAo bm9kZS1pcyAifCIpKQogICAgICAgICAgIGNvbHVtbi0wIGJpc29uLXRzLW1vZGUtaW5kZW50 LW9mZnNldCkKICAgICAgICAgICgoYW5kIChwYXJlbnQtaXMgIl5ncmFtbWFyX3J1bGUkIikK ICAgICAgICAgICAgICAgIChub3QgKG5vZGUtaXMgIl5ncmFtbWFyX3J1bGVfZGVjbGFyYXRp b24kIikpCiAgICAgICAgICAgICAgICAobm90IChub2RlLWlzICJeYWN0aW9uJCIpKSkKICAg ICAgICAgICBjb2x1bW4tMCAsKCsgYmlzb24tdHMtbW9kZS1pbmRlbnQtb2Zmc2V0IDIpKQog ICAgICAgICAgKChvcgogICAgICAgICAgICAobm9kZS1pcyAiXmFjdGlvbiQiKQogICAgICAg ICAgICAobm9kZS1pcyAiXn0kIikpCiAgICAgICAgICAgY29sdW1uLTAgMTIpCiAgICAgICAg ICA7OyBTZXQgJyUlJyBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBsaW5lCiAgICAgICAgICAo KG9yCiAgICAgICAgICAgIChhbmQgKHBhcmVudC1pcyAiXmdyYW1tYXJfcnVsZXNfc2VjdGlv biQiKQogICAgICAgICAgICAgICAgIChub2RlLWlzICIlJSIpKQogICAgICAgICAgICAobm9k ZS1pcyAiXmdyYW1tYXJfcnVsZXNfc2VjdGlvbiQiKSkKICAgICAgICAgICBjb2x1bW4tMCAw KQogICAgICAgICAgKG5vLW5vZGUgcGFyZW50LWJvbCAwKSkpKQogICAgYCgoYmlzb24gLiAs Y29tbW9uKQogICAgICA7OyBJbXBvcnQgYW5kIG92ZXJyaWRlIGVtYmVkZGVkIGxhbmd1YWdl cyBydWxlcyB0byBhZGQgYW4gb2Zmc2V0CiAgICAgICwocGNhc2UgYmlzb24tdHMtbW9kZS1l bWJlZGRlZC1sYW5ndWFnZQogICAgICAgICAoJ2MgYChjCiAgICAgICAgICAgICAgICgoYmlz b24tdHMtbW9kZS0tYmlzb24tbWF0Y2hlci1hY3Rpb24gInRyYW5zbGF0aW9uX3VuaXQiKQog ICAgICAgICAgICAgICAgYmlzb24tdHMtbW9kZS0tYmlzb24tcGFyZW50ICxiaXNvbi10cy1t b2RlLWluZGVudC1vZmZzZXQpCiAgICAgICAgICAgICAgICgoYmlzb24tdHMtbW9kZS0tYmlz b24tbWF0Y2hlci1ub3QtZXBpbG9ndWUgInRyYW5zbGF0aW9uX3VuaXQiKQogICAgICAgICAg ICAgICAgY29sdW1uLTAgLGJpc29uLXRzLW1vZGUtaW5kZW50LW9mZnNldCkKICAgICAgICAg ICAgICAgLEAoYWxpc3QtZ2V0ICdjIChjLXRzLW1vZGUtLWdldC1pbmRlbnQtc3R5bGUgJ2Mp KSkpCiAgICAgICAgICgnY3BwIGAoY3BwCiAgICAgICAgICAgICAgICAgKChiaXNvbi10cy1t b2RlLS1iaXNvbi1tYXRjaGVyLWFjdGlvbiAidHJhbnNsYXRpb25fdW5pdCIpCiAgICAgICAg ICAgICAgICAgIGJpc29uLXRzLW1vZGUtLWJpc29uLXBhcmVudCAsYmlzb24tdHMtbW9kZS1p bmRlbnQtb2Zmc2V0KQogICAgICAgICAgICAgICAgICgoYmlzb24tdHMtbW9kZS0tYmlzb24t bWF0Y2hlci1ub3QtZXBpbG9ndWUgInRyYW5zbGF0aW9uX3VuaXQiKQogICAgICAgICAgICAg ICAgICBjb2x1bW4tMCAsYmlzb24tdHMtbW9kZS1pbmRlbnQtb2Zmc2V0KQogICAgICAgICAg ICAgICAgICxAKGFsaXN0LWdldCAnY3BwIChjLXRzLW1vZGUtLWdldC1pbmRlbnQtc3R5bGUg J2NwcCkpKSkKICAgICAgICAgKCdqYXZhIGAoamF2YQogICAgICAgICAgICAgICAgICAoKGJp c29uLXRzLW1vZGUtLWJpc29uLW1hdGNoZXItYWN0aW9uICJwcm9ncmFtIikKICAgICAgICAg ICAgICAgICAgIGJpc29uLXRzLW1vZGUtLWJpc29uLXBhcmVudCAsYmlzb24tdHMtbW9kZS1p bmRlbnQtb2Zmc2V0KQogICAgICAgICAgICAgICAgICAoKGJpc29uLXRzLW1vZGUtLWJpc29u LW1hdGNoZXItbm90LWVwaWxvZ3VlICJwcm9ncmFtIikKICAgICAgICAgICAgICAgICAgIGNv bHVtbi0wICxiaXNvbi10cy1tb2RlLWluZGVudC1vZmZzZXQpCiAgICAgICAgICAgICAgICAg ICxAamF2YS10cy1tb2RlLS1pbmRlbnQtcnVsZXMpKSkpKSkKCgooZGVmaW5lLWRlcml2ZWQt bW9kZSBiaXNvbi10cy1tb2RlIHByb2ctbW9kZSAiQmlzb24iCiAgIkEgbWFqb3ItbW9kZSBm b3IgQmlzb24gYmFzZWQgb24gdHJlZS1zaXR0ZXIuIgogICh3aGVuICh0cmVlc2l0LXJlYWR5 LXAgJ2Jpc29uKQogICAgKHVubGVzcyBiaXNvbi10cy1tb2RlLWVtYmVkZGVkLWxhbmd1YWdl CiAgICAgIChzZXRxIGJpc29uLXRzLW1vZGUtZW1iZWRkZWQtbGFuZ3VhZ2UgKGJpc29uLXRz LW1vZGUtLWRldGVjdC1sYW5ndWFnZSkpKQoKICAgIDs7IFJlcXVpcmUgb25seSBpZiBuZWVk ZWQsIHRvIGF2b2lkIHdhcm5pbmdzIGlmIGEgZ3JhbW1hciBpcyBub3QKICAgIDs7IGluc3Rh bGxlZCBidXQgbm90IHVzZWQuCiAgICAocGNhc2UgYmlzb24tdHMtbW9kZS1lbWJlZGRlZC1s YW5ndWFnZQogICAgICAoJ2MgKHJlcXVpcmUgJ2MtdHMtbW9kZSkpCiAgICAgICgnY3BwIChy ZXF1aXJlICdjLXRzLW1vZGUpKQogICAgICAoJ2phdmEgKHJlcXVpcmUgJ2phdmEtdHMtbW9k ZSkpKQoKICAgIChzZXRxLWxvY2FsIHRyZWVzaXQtZm9udC1sb2NrLXNldHRpbmdzCiAgICAg ICAgICAgICAgICAoYXBwZW5kIChiaXNvbi10cy1tb2RlLS1mb250LWxvY2stc2V0dGluZ3Mg J2Jpc29uKQogICAgICAgICAgICAgICAgICAgICAgICAocGNhc2UgYmlzb24tdHMtbW9kZS1l bWJlZGRlZC1sYW5ndWFnZQogICAgICAgICAgICAgICAgICAgICAgICAgICgnYyAoYy10cy1t b2RlLS1mb250LWxvY2stc2V0dGluZ3MgJ2MpKQogICAgICAgICAgICAgICAgICAgICAgICAg ICgnY3BwIChjLXRzLW1vZGUtLWZvbnQtbG9jay1zZXR0aW5ncyAnY3BwKSkKICAgICAgICAg ICAgICAgICAgICAgICAgICAoJ2phdmEgamF2YS10cy1tb2RlLS1mb250LWxvY2stc2V0dGlu Z3MpKSkpCgogICAgKHNldHEtbG9jYWwgdHJlZXNpdC1mb250LWxvY2stZmVhdHVyZS1saXN0 CiAgICAgICAgICAgICAgICAoaWYgYmlzb24tdHMtbW9kZS1lbWJlZGRlZC1sYW5ndWFnZQog ICAgICAgICAgICAgICAgICAgIChiaXNvbi10cy1tb2RlLS1tZXJnZS1mZWF0dXJlLWxpc3Rz CiAgICAgICAgICAgICAgICAgICAgIGJpc29uLXRzLW1vZGUtLWZvbnQtbG9jay1mZWF0dXJl LWxpc3QKICAgICAgICAgICAgICAgICAgICAgKHBjYXNlIGJpc29uLXRzLW1vZGUtZW1iZWRk ZWQtbGFuZ3VhZ2UKICAgICAgICAgICAgICAgICAgICAgICAoJ2MgYy10cy1tb2RlLS1mZWF0 dXJlLWxpc3QpCiAgICAgICAgICAgICAgICAgICAgICAgKCdjcHAgYy10cy1tb2RlLS1mZWF0 dXJlLWxpc3QpCiAgICAgICAgICAgICAgICAgICAgICAgKCdqYXZhIGphdmEtdHMtbW9kZS0t ZmVhdHVyZS1saXN0KSkpCiAgICAgICAgICAgICAgICAgIGJpc29uLXRzLW1vZGUtLWZvbnQt bG9jay1mZWF0dXJlLWxpc3QpKQoKICAgIChzZXRxLWxvY2FsIHRyZWVzaXQtc2ltcGxlLWlt ZW51LXNldHRpbmdzCiAgICAgICAgICAgICAgICBgKCgiR3JhbW1hciIKICAgICAgICAgICAg ICAgICAgICJcXGBncmFtbWFyX3J1bGVfZGVjbGFyYXRpb25cXCciCiAgICAgICAgICAgICAg ICAgICBuaWwKICAgICAgICAgICAgICAgICAgIChsYW1iZGEgKG5vZGUpICh0cmVlc2l0LW5v ZGUtdGV4dCBub2RlIHQpKSkpKQoKICAgIChjLXRzLWNvbW1vbi1jb21tZW50LXNldHVwKQoK ICAgIChzZXRxLWxvY2FsIHRyZWVzaXQtc2ltcGxlLWluZGVudC1ydWxlcwogICAgICAgICAg ICAgICAgKGJpc29uLXRzLW1vZGUtLWluZGVudC1ydWxlcykpCgogICAgKHNldHEtbG9jYWwg dHJlZXNpdC1sYW5ndWFnZS1hdC1wb2ludC1mdW5jdGlvbiAnYmlzb24tdHMtbW9kZS0tbGFu Z3VhZ2UtYXQtcG9pbnQtZnVuY3Rpb24pCgoKICAgIChpZiBiaXNvbi10cy1tb2RlLWVtYmVk ZGVkLWxhbmd1YWdlCgkoc2V0cS1sb2NhbCB0cmVlc2l0LXJhbmdlLXNldHRpbmdzICAgICAg ICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAodHJlZXNpdC1yYW5nZS1ydWxl cwogICAgICAgICAgICAgICAgICAgICA6ZW1iZWQgYmlzb24tdHMtbW9kZS1lbWJlZGRlZC1s YW5ndWFnZQogICAgICAgICAgICAgICAgICAgICA6aG9zdCAnYmlzb24KICAgICAgICAgICAg ICAgICAgICAgOmxvY2FsIHQKICAgICAgICAgICAgICAgICAgICAgJygoZW1iZWRkZWRfY29k ZSkgQGNhcHR1cmUpKSkKICAgICAgKHRyZWVzaXQtcGFyc2VyLWNyZWF0ZSAnYmlzb24pKQoK ICAgICh0cmVlc2l0LW1ham9yLW1vZGUtc2V0dXApKSkKCihwcm92aWRlICdiaXNvbi10cy1t b2RlKQo7OzsgYmlzb24tdHMtbW9kZS5lbCBlbmRzIGhlcmUK --------------Lq0ToYflO0LiYFVdltsp7jo1--