mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 15:43:01 +00:00 
			
		
		
		
	Add rules for nth and only-tags. Address #1503
These rules allow selecting from a number of sub-captures while dropping the rest. `nth` is more succinct in many cases, but `only-tags` is more general and corresponds to an internal mechanism already present.
This commit is contained in:
		| @@ -588,6 +588,7 @@ JANET_CORE_FN(cfun_filewatch_make, | ||||
|         "* `:wd-path` -- the string path for watched directory of file. For files, will be the same as `:file-name`, and for directories, will be the same as `:dir-name`.\n\n" | ||||
|         "* `:cookie` -- a randomized integer used to associate related events, such as :moved-from and :moved-to events.\n\n" | ||||
|         "") { | ||||
|     janet_sandbox_assert(JANET_SANDBOX_FS_READ); | ||||
|     janet_arity(argc, 1, -1); | ||||
|     JanetChannel *channel = janet_getchannel(argv, 0); | ||||
|     JanetWatcher *watcher = janet_abstract(&janet_filewatch_at, sizeof(JanetWatcher)); | ||||
|   | ||||
| @@ -465,6 +465,16 @@ tail: | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         case RULE_ONLY_TAGS: { | ||||
|             CapState cs = cap_save(s); | ||||
|             down1(s); | ||||
|             const uint8_t *result = peg_rule(s, s->bytecode + rule[1], text); | ||||
|             up1(s); | ||||
|             if (!result) return NULL; | ||||
|             cap_load_keept(s, cs); | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         case RULE_GROUP: { | ||||
|             uint32_t tag = rule[2]; | ||||
|             int oldmode = s->mode; | ||||
| @@ -486,6 +496,29 @@ tail: | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         case RULE_NTH: { | ||||
|             uint32_t nth = rule[1]; | ||||
|             uint32_t tag = rule[3]; | ||||
|             int oldmode = s->mode; | ||||
|             CapState cs = cap_save(s); | ||||
|             s->mode = PEG_MODE_NORMAL; | ||||
|             down1(s); | ||||
|             const uint8_t *result = peg_rule(s, s->bytecode + rule[2], text); | ||||
|             up1(s); | ||||
|             s->mode = oldmode; | ||||
|             if (!result) return NULL; | ||||
|             int32_t num_sub_captures = s->captures->count - cs.cap; | ||||
|             Janet cap; | ||||
|             if (num_sub_captures > (int32_t) nth) { | ||||
|                 cap = s->captures->data[cs.cap + nth]; | ||||
|             } else { | ||||
|                 return NULL; | ||||
|             } | ||||
|             cap_load_keept(s, cs); | ||||
|             pushcap(s, cap, tag); | ||||
|             return result; | ||||
|         } | ||||
|  | ||||
|         case RULE_SUB: { | ||||
|             const uint8_t *text_start = text; | ||||
|             const uint32_t *rule_window = s->bytecode + rule[1]; | ||||
| @@ -1061,6 +1094,9 @@ static void spec_thru(Builder *b, int32_t argc, const Janet *argv) { | ||||
| static void spec_drop(Builder *b, int32_t argc, const Janet *argv) { | ||||
|     spec_onerule(b, argc, argv, RULE_DROP); | ||||
| } | ||||
| static void spec_only_tags(Builder *b, int32_t argc, const Janet *argv) { | ||||
|     spec_onerule(b, argc, argv, RULE_ONLY_TAGS); | ||||
| } | ||||
|  | ||||
| /* Rule of the form [rule, tag] */ | ||||
| static void spec_cap1(Builder *b, int32_t argc, const Janet *argv, uint32_t op) { | ||||
| @@ -1084,6 +1120,15 @@ static void spec_unref(Builder *b, int32_t argc, const Janet *argv) { | ||||
|     spec_cap1(b, argc, argv, RULE_UNREF); | ||||
| } | ||||
|  | ||||
| static void spec_nth(Builder *b, int32_t argc, const Janet *argv) { | ||||
|     peg_arity(b, argc, 2, 3); | ||||
|     Reserve r = reserve(b, 4); | ||||
|     uint32_t nth = peg_getnat(b, argv[0]); | ||||
|     uint32_t rule = peg_compile1(b, argv[1]); | ||||
|     uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0; | ||||
|     emit_3(r, RULE_NTH, nth, rule, tag); | ||||
| } | ||||
|  | ||||
| static void spec_capture_number(Builder *b, int32_t argc, const Janet *argv) { | ||||
|     peg_arity(b, argc, 1, 3); | ||||
|     Reserve r = reserve(b, 4); | ||||
| @@ -1262,7 +1307,9 @@ static const SpecialPair peg_specials[] = { | ||||
|     {"line", spec_line}, | ||||
|     {"look", spec_look}, | ||||
|     {"not", spec_not}, | ||||
|     {"nth", spec_nth}, | ||||
|     {"number", spec_capture_number}, | ||||
|     {"only-tags", spec_only_tags}, | ||||
|     {"opt", spec_opt}, | ||||
|     {"position", spec_position}, | ||||
|     {"quote", spec_capture}, | ||||
| @@ -1619,6 +1666,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) { | ||||
|                 break; | ||||
|             case RULE_ERROR: | ||||
|             case RULE_DROP: | ||||
|             case RULE_ONLY_TAGS: | ||||
|             case RULE_NOT: | ||||
|             case RULE_TO: | ||||
|             case RULE_THRU: | ||||
| @@ -1632,6 +1680,12 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) { | ||||
|                 if (rule[1] > JANET_MAX_READINT_WIDTH) goto bad; | ||||
|                 i += 3; | ||||
|                 break; | ||||
|             case RULE_NTH: | ||||
|                 /* [nth, rule, tag] */ | ||||
|                 if (rule[2] >= blen) goto bad; | ||||
|                 op_flags[rule[2]] |= 0x01; | ||||
|                 i += 4; | ||||
|                 break; | ||||
|             default: | ||||
|                 goto bad; | ||||
|         } | ||||
|   | ||||
| @@ -2180,7 +2180,9 @@ typedef enum { | ||||
|     RULE_UNREF,        /* [rule, tag] */ | ||||
|     RULE_CAPTURE_NUM,  /* [rule, tag] */ | ||||
|     RULE_SUB,          /* [rule, rule] */ | ||||
|     RULE_SPLIT         /* [rule, rule] */ | ||||
|     RULE_SPLIT,        /* [rule, rule] */ | ||||
|     RULE_NTH,          /* [nth, rule, tag] */ | ||||
|     RULE_ONLY_TAGS,    /* [rule] */ | ||||
| } JanetPegOpcod; | ||||
|  | ||||
| typedef struct { | ||||
|   | ||||
| @@ -664,6 +664,8 @@ | ||||
|   @[]) "peg if not") | ||||
|  | ||||
| (defn test [name peg input expected] | ||||
|   (assert-no-error "compile peg" (peg/compile peg)) | ||||
|   (assert-no-error "marshal/unmarshal peg" (-> peg marshal unmarshal)) | ||||
|   (assert (deep= (peg/match peg input) expected) name)) | ||||
|  | ||||
| (test "sub: matches the same input twice" | ||||
| @@ -756,5 +758,19 @@ | ||||
|   "a,b,c" | ||||
|   @["a" "b" "c"]) | ||||
|  | ||||
| (test "nth 1" | ||||
|   ~{:prefix (number :d+ nil :n) | ||||
|     :word '(lenprefix (-> :n) :w) | ||||
|     :main (some (nth 1 (* :prefix ":" :word)))} | ||||
|   "5:apple6:banana6:cherry" | ||||
|   @["apple" "banana" "cherry"]) | ||||
|  | ||||
| (test "only-tags 1" | ||||
|   ~{:prefix (number :d+ nil :n) | ||||
|     :word (capture (lenprefix (-> :n) :w) :W) | ||||
|     :main (some (* (only-tags (* :prefix ":" :word)) (-> :W)))} | ||||
|   "5:apple6:banana6:cherry" | ||||
|   @["apple" "banana" "cherry"]) | ||||
|  | ||||
| (end-suite) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose