Skip to content

Commit 9694aee

Browse files
committed
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.
1 parent 2697b0e commit 9694aee

File tree

4 files changed

+74
-1
lines changed

4 files changed

+74
-1
lines changed

src/core/filewatch.c

+1
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,7 @@ JANET_CORE_FN(cfun_filewatch_make,
588588
"* `: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"
589589
"* `:cookie` -- a randomized integer used to associate related events, such as :moved-from and :moved-to events.\n\n"
590590
"") {
591+
janet_sandbox_assert(JANET_SANDBOX_FS_READ);
591592
janet_arity(argc, 1, -1);
592593
JanetChannel *channel = janet_getchannel(argv, 0);
593594
JanetWatcher *watcher = janet_abstract(&janet_filewatch_at, sizeof(JanetWatcher));

src/core/peg.c

+54
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,16 @@ static const uint8_t *peg_rule(
465465
return result;
466466
}
467467

468+
case RULE_ONLY_TAGS: {
469+
CapState cs = cap_save(s);
470+
down1(s);
471+
const uint8_t *result = peg_rule(s, s->bytecode + rule[1], text);
472+
up1(s);
473+
if (!result) return NULL;
474+
cap_load_keept(s, cs);
475+
return result;
476+
}
477+
468478
case RULE_GROUP: {
469479
uint32_t tag = rule[2];
470480
int oldmode = s->mode;
@@ -486,6 +496,29 @@ static const uint8_t *peg_rule(
486496
return result;
487497
}
488498

499+
case RULE_NTH: {
500+
uint32_t nth = rule[1];
501+
uint32_t tag = rule[3];
502+
int oldmode = s->mode;
503+
CapState cs = cap_save(s);
504+
s->mode = PEG_MODE_NORMAL;
505+
down1(s);
506+
const uint8_t *result = peg_rule(s, s->bytecode + rule[2], text);
507+
up1(s);
508+
s->mode = oldmode;
509+
if (!result) return NULL;
510+
int32_t num_sub_captures = s->captures->count - cs.cap;
511+
Janet cap;
512+
if (num_sub_captures > (int32_t) nth) {
513+
cap = s->captures->data[cs.cap + nth];
514+
} else {
515+
return NULL;
516+
}
517+
cap_load_keept(s, cs);
518+
pushcap(s, cap, tag);
519+
return result;
520+
}
521+
489522
case RULE_SUB: {
490523
const uint8_t *text_start = text;
491524
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) {
10611094
static void spec_drop(Builder *b, int32_t argc, const Janet *argv) {
10621095
spec_onerule(b, argc, argv, RULE_DROP);
10631096
}
1097+
static void spec_only_tags(Builder *b, int32_t argc, const Janet *argv) {
1098+
spec_onerule(b, argc, argv, RULE_ONLY_TAGS);
1099+
}
10641100

10651101
/* Rule of the form [rule, tag] */
10661102
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) {
10841120
spec_cap1(b, argc, argv, RULE_UNREF);
10851121
}
10861122

1123+
static void spec_nth(Builder *b, int32_t argc, const Janet *argv) {
1124+
peg_arity(b, argc, 2, 3);
1125+
Reserve r = reserve(b, 4);
1126+
uint32_t nth = peg_getnat(b, argv[0]);
1127+
uint32_t rule = peg_compile1(b, argv[1]);
1128+
uint32_t tag = (argc == 3) ? emit_tag(b, argv[2]) : 0;
1129+
emit_3(r, RULE_NTH, nth, rule, tag);
1130+
}
1131+
10871132
static void spec_capture_number(Builder *b, int32_t argc, const Janet *argv) {
10881133
peg_arity(b, argc, 1, 3);
10891134
Reserve r = reserve(b, 4);
@@ -1262,7 +1307,9 @@ static const SpecialPair peg_specials[] = {
12621307
{"line", spec_line},
12631308
{"look", spec_look},
12641309
{"not", spec_not},
1310+
{"nth", spec_nth},
12651311
{"number", spec_capture_number},
1312+
{"only-tags", spec_only_tags},
12661313
{"opt", spec_opt},
12671314
{"position", spec_position},
12681315
{"quote", spec_capture},
@@ -1619,6 +1666,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
16191666
break;
16201667
case RULE_ERROR:
16211668
case RULE_DROP:
1669+
case RULE_ONLY_TAGS:
16221670
case RULE_NOT:
16231671
case RULE_TO:
16241672
case RULE_THRU:
@@ -1632,6 +1680,12 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
16321680
if (rule[1] > JANET_MAX_READINT_WIDTH) goto bad;
16331681
i += 3;
16341682
break;
1683+
case RULE_NTH:
1684+
/* [nth, rule, tag] */
1685+
if (rule[2] >= blen) goto bad;
1686+
op_flags[rule[2]] |= 0x01;
1687+
i += 4;
1688+
break;
16351689
default:
16361690
goto bad;
16371691
}

src/include/janet.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -2180,7 +2180,9 @@ typedef enum {
21802180
RULE_UNREF, /* [rule, tag] */
21812181
RULE_CAPTURE_NUM, /* [rule, tag] */
21822182
RULE_SUB, /* [rule, rule] */
2183-
RULE_SPLIT /* [rule, rule] */
2183+
RULE_SPLIT, /* [rule, rule] */
2184+
RULE_NTH, /* [nth, rule, tag] */
2185+
RULE_ONLY_TAGS, /* [rule] */
21842186
} JanetPegOpcod;
21852187

21862188
typedef struct {

test/suite-peg.janet

+16
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,8 @@
664664
@[]) "peg if not")
665665

666666
(defn test [name peg input expected]
667+
(assert-no-error "compile peg" (peg/compile peg))
668+
(assert-no-error "marshal/unmarshal peg" (-> peg marshal unmarshal))
667669
(assert (deep= (peg/match peg input) expected) name))
668670

669671
(test "sub: matches the same input twice"
@@ -756,5 +758,19 @@
756758
"a,b,c"
757759
@["a" "b" "c"])
758760

761+
(test "nth 1"
762+
~{:prefix (number :d+ nil :n)
763+
:word '(lenprefix (-> :n) :w)
764+
:main (some (nth 1 (* :prefix ":" :word)))}
765+
"5:apple6:banana6:cherry"
766+
@["apple" "banana" "cherry"])
767+
768+
(test "only-tags 1"
769+
~{:prefix (number :d+ nil :n)
770+
:word (capture (lenprefix (-> :n) :w) :W)
771+
:main (some (* (only-tags (* :prefix ":" :word)) (-> :W)))}
772+
"5:apple6:banana6:cherry"
773+
@["apple" "banana" "cherry"])
774+
759775
(end-suite)
760776

0 commit comments

Comments
 (0)