1
0
mirror of https://github.com/janet-lang/janet synced 2026-04-10 08:51:26 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Calvin Rose
bd3f48d26a Add matchcap combinator to pegs.
This allows running rules on captures of pegs. This is flexible, but
loses some guarantees about rules only operating on the source string.
This means that line and column information will be incorrect inside the
matchcap combinator.
2021-02-23 17:58:27 -06:00
13 changed files with 115 additions and 212 deletions

View File

@@ -1,18 +1,6 @@
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased - ???
- Increase default nesting depth of pretty printing to `JANET_RECURSION_GUARD`
- Update meson.build
- Add option to automatically add shebang line in installed scripts with `jpm`.
- Add `partition-by` and `group-by` to the core.
- Sort keys in pretty printing output.
## 1.15.3 - 2021-02-28
- Fix a fiber bug that occured in deeply nested fibers
- Add `unref` combinator to pegs.
- Small docstring changes.
## 1.15.2 - 2021-02-15
- Fix bug in windows version of `os/spawn` and `os/execute` with setting environment variables.
- Fix documentation typos.

View File

@@ -27,7 +27,7 @@ PREFIX?=/usr/local
INCLUDEDIR?=$(PREFIX)/include
BINDIR?=$(PREFIX)/bin
LIBDIR?=$(PREFIX)/lib
JANET_BUILD?="\"$(shell git log --pretty=format:'%h' -n 1 2> /dev/null || echo local)\""
JANET_BUILD?="\"$(shell git log --pretty=format:'%h' -n 2> /dev/null || echo local)\""
CLIBS=-lm -lpthread
JANET_TARGET=build/janet
JANET_LIBRARY=build/libjanet.so

17
jpm
View File

@@ -44,7 +44,7 @@
(defn- try-real [path]
"If os/realpath fails just use normal path."
(try (os/realpath path) ([_] path)))
(try (os/realpath) ([_] path)))
(defn- install-paths []
{:headerpath (try-real (string exe-dir "/../include/janet"))
@@ -1080,15 +1080,12 @@ int main(int argc, const char **argv) {
(install-rule dest (dyn :binpath JANET_BINPATH))))))
(defn declare-binscript
``Declare a janet file to be installed as an executable script. Creates
"Declare a janet file to be installed as an executable script. Creates
a shim on windows. If hardcode is true, will insert code into the script
such that it will run correctly even when JANET_PATH is changed. if auto-shebang
is truthy, will also automatically insert a correct shebang line.
``
[&keys {:main main :hardcode-syspath hardcode :is-janet is-janet}]
such that it will run correctly even when JANET_PATH is changed."
[&keys {:main main :hardcode-syspath hardcode}]
(def binpath (dyn :binpath JANET_BINPATH))
(def auto-shebang (and is-janet (dyn :auto-shebang)))
(if (or auto-shebang hardcode)
(if hardcode
(let [syspath (dyn :modpath JANET_MODPATH)]
(def parts (peg/match path-splitter main))
(def name (last parts))
@@ -1100,9 +1097,7 @@ int main(int argc, const char **argv) {
(def first-line (:read f :line))
(def second-line (string/format "(put root-env :syspath %v)\n" syspath))
(def rest (:read f :all))
(string (if auto-shebang
(string "#!" (dyn :binpath JANET_BINPATH) "/janet\n"))
first-line (if hardcode second-line) rest)))
(string first-line second-line rest)))
(create-dirs path)
(spit path contents)
(unless is-win (shell "chmod" "+x" path))))

4
jpm.1
View File

@@ -42,10 +42,6 @@ Prevents jpm from going to network to get dependencies - all dependencies should
Use this flag with the deps and update-pkgs subcommands. This is not a surefire way to prevent a build script from accessing
the network, for example, a build script that invokes curl will still have network access.
.TP
.BR \-\-auto\-shebang
Prepends installed scripts with a generated shebang line, such that they will use a janet binary located in JANET_BINPATH.
.SH OPTIONS
.TP

View File

@@ -19,8 +19,8 @@
# IN THE SOFTWARE.
project('janet', 'c',
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
version : '1.15.3')
default_options : ['c_std=c99', 'b_lundef=false', 'default_library=both'],
version : '1.15.2')
# Global settings
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
@@ -33,7 +33,7 @@ dl_dep = cc.find_library('dl', required : false)
thread_dep = dependency('threads')
# Link options
if get_option('default_library') != 'static' and build_machine.system() != 'windows'
if build_machine.system() != 'windows'
add_project_link_arguments('-rdynamic', language : 'c')
endif
@@ -244,7 +244,6 @@ janet_dep = declare_dependency(include_directories : incdir,
# pkgconfig
pkg = import('pkgconfig')
pkg.generate(libjanet,
subdirs: 'janet',
description: 'Library for the Janet programming language.')
# Installation

View File

@@ -778,28 +778,24 @@
a)
(defn sort
``Sort `ind` in-place, and return it. Uses quick-sort and is not a stable sort.
If a `before?` comparator function is provided, sorts elements using that,
otherwise uses `<`.``
[ind &opt before?]
(sort-help ind 0 (- (length ind) 1) (or before? <)))
"Sort an array in-place. Uses quick-sort and is not a stable sort."
[a &opt before?]
(sort-help a 0 (- (length a) 1) (or before? <)))
(defn sort-by
``Returns `ind` sorted by calling
a function `f` on each element and comparing the result with `<`.``
a function `f` on each element and comparing the result with <.``
[f ind]
(sort ind (fn [x y] (< (f x) (f y)))))
(defn sorted
``Returns a new sorted array without modifying the old one.
If a `before?` comparator function is provided, sorts elements using that,
otherwise uses `<`.``
"Returns a new sorted array without modifying the old one."
[ind &opt before?]
(sort (array/slice ind) before?))
(defn sorted-by
``Returns a new sorted array that compares elements by invoking
a function `f` on each element and comparing the result with `<`.``
a function `f` on each element and comparing the result with <.``
[f ind]
(sorted ind (fn [x y] (< (f x) (f y)))))
@@ -1451,36 +1447,6 @@
(set (freqs x) (if n (+ 1 n) 1)))
freqs)
(defn group-by
``Group elements of `ind` by a function `f` and put the results into a table. The keys of
the table are the distinct return values of `f`, and the values are arrays of all elements of `ind`
that are equal to that value.``
[f ind]
(def ret @{})
(each x ind
(def y (f x))
(if-let [arr (get ret y)]
(array/push arr x)
(put ret y @[x])))
ret)
(defn partition-by
``Partition elements of a sequential data structure by a representative function `f`. Partitions
split when `(f x)` changes values when iterating to the next element `x` of `ind`. Returns a new array
of arrays.``
[f ind]
(def ret @[])
(var span nil)
(var category nil)
(var is-new true)
(each x ind
(def y (f x))
(cond
is-new (do (set is-new false) (set category y) (set span @[x]) (array/push ret span))
(= y category) (array/push span x)
(do (set category y) (set span @[x]) (array/push ret span))))
ret)
(defn interleave
"Returns an array of the first elements of each col, then the second, etc."
[& cols]
@@ -2132,7 +2098,7 @@
:on-parse-error on-parse-error
:fiber-flags guard
:evaluator evaluator
:source default-where
:source where
:parser parser
:read read
:expander expand} opts)
@@ -2142,11 +2108,9 @@
(default on-compile-error bad-compile)
(default on-parse-error bad-parse)
(default evaluator (fn evaluate [x &] (x)))
(default default-where "<anonymous>")
(default where "<anonymous>")
(default guard :ydt)
(var where default-where)
# Evaluate 1 source form in a protected manner
(defn eval1 [source &opt l c]
(def source (if expand (expand source) source))
@@ -2199,18 +2163,11 @@
(while parser-not-done
(if (env :exit) (break))
(buffer/clear buf)
(match (chunks buf p)
:cancel
(if (= (chunks buf p) :cancel)
(do
# A :cancel chunk represents a cancelled form in the REPL, so reset.
(:flush p)
(buffer/clear buf))
[:source new-where]
(if (string? new-where)
(set where new-where)
(set where default-where))
(do
(var pindex 0)
(var pstatus nil)

View File

@@ -5,9 +5,9 @@
#define JANET_VERSION_MAJOR 1
#define JANET_VERSION_MINOR 15
#define JANET_VERSION_PATCH 3
#define JANET_VERSION_PATCH 2
#define JANET_VERSION_EXTRA ""
#define JANET_VERSION "1.15.3"
#define JANET_VERSION "1.15.2"
/* #define JANET_BUILD "local" */

View File

@@ -2085,7 +2085,7 @@ static const JanetReg os_cfuns[] = {
#ifndef JANET_NO_PROCESSES
{
"os/execute", os_execute,
JDOC("(os/execute args &opt flags env)\n\n"
JDOC("(os/execute args &opts flags env)\n\n"
"Execute a program on the system and pass it string arguments. `flags` "
"is a keyword that modifies how the program will execute.\n\n"
"* :e - enables passing an environment to the program. Without :e, the "
@@ -2105,7 +2105,7 @@ static const JanetReg os_cfuns[] = {
},
{
"os/spawn", os_spawn,
JDOC("(os/spawn args &opt flags env)\n\n"
JDOC("(os/spawn args &opts flags env)\n\n"
"Execute a program on the system and return a handle to the process. Otherwise, the "
"same arguments as os/execute. Does not wait for the process.")
},

View File

@@ -596,28 +596,65 @@ tail:
return text + width;
}
case RULE_UNREF: {
int32_t tcap = s->tags->count;
case RULE_MATCHCAP: {
const uint32_t *rule_a = s->bytecode + rule[1];
const uint32_t *rule_b = s->bytecode + rule[2];
int oldmode = s->mode;
CapState cs = cap_save(s);
s->mode = PEG_MODE_NORMAL;
int32_t old_cap = s->captures->count;
down1(s);
const uint8_t *result = peg_rule(s, s->bytecode + rule[1], text);
const uint8_t *b_result = peg_rule(s, rule_b, text);
up1(s);
if (!result) return NULL;
int32_t final_tcap = s->tags->count;
/* Truncate tagged captures to not include items of the given tag */
int32_t w = tcap;
/* If no tag is given, drop ALL tagged captures */
if (rule[2]) {
for (int32_t i = tcap; i < final_tcap; i++) {
if (s->tags->data[i] != (0xFF & rule[2])) {
s->tags->data[w] = s->tags->data[i];
s->tagged_captures->data[w] = s->tagged_captures->data[i];
w++;
}
s->mode = oldmode;
if (!b_result) return NULL;
int32_t new_cap = s->captures->count;
/* Check for bad captures */
for (int32_t i = old_cap; i < new_cap; i++) {
Janet capture = s->captures->data[i];
if (!janet_checktype(capture, JANET_STRING)) {
return NULL;
}
}
s->tags->count = w;
s->tagged_captures->count = w;
return result;
/* Save captures to temporary buffer */
Janet *temp_mem = janet_smalloc(sizeof(Janet) * (new_cap - old_cap));
for (int32_t i = old_cap; i < new_cap; i++) {
temp_mem[i - old_cap] = s->captures->data[i];
}
cap_load(s, cs);
for (int32_t i = old_cap; i < new_cap; i++) {
Janet capture = temp_mem[i - old_cap];
const uint8_t *str = janet_unwrap_string(capture);
PegState subs;
subs.mode = PEG_MODE_NORMAL;
subs.text_start = str;
subs.text_end = str + janet_string_length(str);
subs.depth = s->depth - 1;
subs.captures = s->captures;
subs.tagged_captures = s->tagged_captures;
subs.scratch = janet_buffer(10);
subs.tags = s->tags;
subs.constants = s->constants;
subs.bytecode = s->bytecode;
subs.linemap = NULL;
subs.linemaplen = -1;
subs.has_backref = s->has_backref;
subs.extrac = s->extrac;
subs.extrav = s->extrav;
const uint8_t *a_result = peg_rule(&subs, rule_a, str);
if (NULL == a_result) {
janet_sfree(temp_mem);
return NULL;
}
}
janet_sfree(temp_mem);
return b_result;
}
}
@@ -868,6 +905,9 @@ static void spec_ifnot(Builder *b, int32_t argc, const Janet *argv) {
static void spec_lenprefix(Builder *b, int32_t argc, const Janet *argv) {
spec_branch(b, argc, argv, RULE_LENPREFIX);
}
static void spec_matchcap(Builder *b, int32_t argc, const Janet *argv) {
spec_branch(b, argc, argv, RULE_MATCHCAP);
}
static void spec_between(Builder *b, int32_t argc, const Janet *argv) {
peg_fixarity(b, argc, 3);
@@ -943,15 +983,15 @@ static void spec_error(Builder *b, int32_t argc, const Janet *argv) {
spec_onerule(b, argc, argv, RULE_ERROR);
}
}
static void spec_drop(Builder *b, int32_t argc, const Janet *argv) {
spec_onerule(b, argc, argv, RULE_DROP);
}
static void spec_to(Builder *b, int32_t argc, const Janet *argv) {
spec_onerule(b, argc, argv, RULE_TO);
}
static void spec_thru(Builder *b, int32_t argc, const Janet *argv) {
spec_onerule(b, argc, argv, RULE_THRU);
}
static void spec_drop(Builder *b, int32_t argc, const Janet *argv) {
spec_onerule(b, argc, argv, RULE_DROP);
}
/* Rule of the form [rule, tag] */
static void spec_cap1(Builder *b, int32_t argc, const Janet *argv, uint32_t op) {
@@ -971,9 +1011,6 @@ static void spec_accumulate(Builder *b, int32_t argc, const Janet *argv) {
static void spec_group(Builder *b, int32_t argc, const Janet *argv) {
spec_cap1(b, argc, argv, RULE_GROUP);
}
static void spec_unref(Builder *b, int32_t argc, const Janet *argv) {
spec_cap1(b, argc, argv, RULE_UNREF);
}
static void spec_reference(Builder *b, int32_t argc, const Janet *argv) {
peg_arity(b, argc, 1, 2);
@@ -1117,6 +1154,7 @@ static const SpecialPair peg_specials[] = {
{"lenprefix", spec_lenprefix},
{"line", spec_line},
{"look", spec_look},
{"matchcap", spec_matchcap},
{"not", spec_not},
{"opt", spec_opt},
{"position", spec_position},
@@ -1131,7 +1169,6 @@ static const SpecialPair peg_specials[] = {
{"to", spec_to},
{"uint", spec_uint_le},
{"uint-be", spec_uint_be},
{"unref", spec_unref},
};
/* Compile a janet value into a rule and return the rule index. */
@@ -1390,6 +1427,7 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
case RULE_IF:
case RULE_IFNOT:
case RULE_LENPREFIX:
case RULE_MATCHCAP:
/* [rule_a, rule_b (b if not a)] */
if (rule[1] >= blen) goto bad;
if (rule[2] >= blen) goto bad;
@@ -1420,7 +1458,6 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
case RULE_ACCUMULATE:
case RULE_GROUP:
case RULE_CAPTURE:
case RULE_UNREF:
/* [rule, tag] */
if (rule[1] >= blen) goto bad;
op_flags[rule[1]] |= 0x01;

View File

@@ -351,9 +351,6 @@ struct pretty {
int indent;
int flags;
int32_t bufstartlen;
int32_t *keysort_buffer;
int32_t keysort_capacity;
int32_t keysort_start;
JanetTable seen;
};
@@ -597,55 +594,31 @@ static void janet_pretty_one(struct pretty *S, Janet x, int is_dict_value) {
janet_buffer_push_cstring(S->buffer, "...");
} else {
int32_t i = 0, len = 0, cap = 0;
int first_kv_pair = 1;
const JanetKV *kvs = NULL;
int counter = 0;
janet_dictionary_view(x, &kvs, &len, &cap);
if (!istable && !(S->flags & JANET_PRETTY_ONELINE) && len >= JANET_PRETTY_DICT_ONELINE)
janet_buffer_push_u8(S->buffer, ' ');
if (is_dict_value && len >= JANET_PRETTY_DICT_ONELINE) print_newline(S, 0);
int32_t ks_start = S->keysort_start;
/* Ensure buffer is large enough to sort keys. */
int truncated = 0;
int64_t mincap = (int64_t) len + (int64_t) ks_start;
if (mincap > INT32_MAX) {
truncated = 1;
len = 0;
mincap = ks_start;
}
if (S->keysort_capacity < mincap) {
if (mincap >= INT32_MAX / 2) {
S->keysort_capacity = INT32_MAX;
} else {
S->keysort_capacity = mincap * 2;
}
S->keysort_buffer = janet_srealloc(S->keysort_buffer, sizeof(int32_t) * S->keysort_capacity);
if (NULL == S->keysort_buffer) {
JANET_OUT_OF_MEMORY;
for (i = 0; i < cap; i++) {
if (!janet_checktype(kvs[i].key, JANET_NIL)) {
if (counter == JANET_PRETTY_DICT_LIMIT && !(S->flags & JANET_PRETTY_NOTRUNC)) {
print_newline(S, 0);
janet_buffer_push_cstring(S->buffer, "...");
break;
}
if (first_kv_pair) {
first_kv_pair = 0;
} else {
print_newline(S, len < JANET_PRETTY_DICT_ONELINE);
}
janet_pretty_one(S, kvs[i].key, 0);
janet_buffer_push_u8(S->buffer, ' ');
janet_pretty_one(S, kvs[i].value, 1);
counter++;
}
}
janet_sorted_keys(kvs, cap, S->keysort_buffer + ks_start);
S->keysort_start += len;
if (!(S->flags & JANET_PRETTY_NOTRUNC) && (len > JANET_PRETTY_DICT_LIMIT)) {
len = JANET_PRETTY_DICT_LIMIT;
truncated = 1;
}
for (i = 0; i < len; i++) {
if (i) print_newline(S, len < JANET_PRETTY_DICT_ONELINE);
int32_t j = S->keysort_buffer[i + ks_start];
janet_pretty_one(S, kvs[j].key, 0);
janet_buffer_push_u8(S->buffer, ' ');
janet_pretty_one(S, kvs[j].value, 1);
}
if (truncated) {
print_newline(S, 0);
janet_buffer_push_cstring(S->buffer, "...");
}
S->keysort_start = ks_start;
}
S->indent -= 2;
S->depth++;
@@ -668,9 +641,6 @@ static JanetBuffer *janet_pretty_(JanetBuffer *buffer, int depth, int flags, Jan
S.indent = 0;
S.flags = flags;
S.bufstartlen = startlen;
S.keysort_capacity = 0;
S.keysort_buffer = NULL;
S.keysort_start = 0;
janet_table_init(&S.seen, 10);
janet_pretty_one(&S, x, 0);
janet_table_deinit(&S.seen);
@@ -693,9 +663,6 @@ static JanetBuffer *janet_jdn_(JanetBuffer *buffer, int depth, Janet x, int32_t
S.indent = 0;
S.flags = 0;
S.bufstartlen = startlen;
S.keysort_capacity = 0;
S.keysort_buffer = NULL;
S.keysort_start = 0;
janet_table_init(&S.seen, 10);
int res = print_jdn_one(&S, x, depth);
janet_table_deinit(&S.seen);
@@ -855,7 +822,7 @@ void janet_formatbv(JanetBuffer *b, const char *format, va_list args) {
case 'P':
case 'p': { /* janet pretty , precision = depth */
int depth = atoi(precision);
if (depth < 1) depth = JANET_RECURSION_GUARD;
if (depth < 1) depth = 4;
char d = c[-1];
int has_color = (d == 'P') || (d == 'Q') || (d == 'M') || (d == 'N');
int has_oneline = (d == 'Q') || (d == 'q') || (d == 'N') || (d == 'n');
@@ -1007,7 +974,7 @@ void janet_buffer_format(
case 'P':
case 'p': { /* janet pretty , precision = depth */
int depth = atoi(precision);
if (depth < 1) depth = JANET_RECURSION_GUARD;
if (depth < 1) depth = 4;
char d = strfrmt[-1];
int has_color = (d == 'P') || (d == 'Q') || (d == 'M') || (d == 'N');
int has_oneline = (d == 'Q') || (d == 'q') || (d == 'N') || (d == 'n');

View File

@@ -602,38 +602,6 @@ JanetTable *janet_get_core_table(const char *name) {
return janet_unwrap_table(out);
}
/* Sort keys of a dictionary type */
int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffer) {
/* First, put populated indices into index_buffer */
int32_t next_index = 0;
for (int32_t i = 0; i < cap; i++) {
if (!janet_checktype(dict[i].key, JANET_NIL)) {
index_buffer[next_index++] = i;
}
}
/* Next, sort those (simple insertion sort here for now) */
for (int32_t i = 1; i < next_index; i++) {
int32_t index_to_insert = index_buffer[i];
Janet lhs = dict[index_to_insert].key;
for (int32_t j = i - 1; j >= 0; j--) {
index_buffer[j + 1] = index_buffer[j];
Janet rhs = dict[index_buffer[j]].key;
if (janet_compare(lhs, rhs) >= 0) {
index_buffer[j + 1] = index_to_insert;
break;
} else if (j == 0) {
index_buffer[0] = index_to_insert;
}
}
}
/* Return number of indices found */
return next_index;
}
/* Clock shims for various platforms */
#ifdef JANET_GETTIME
/* For macos */

View File

@@ -1630,7 +1630,6 @@ JANET_API Janet janet_wrap_number_safe(double x);
JANET_API int janet_keyeq(Janet x, const char *cstring);
JANET_API int janet_streq(Janet x, const char *cstring);
JANET_API int janet_symeq(Janet x, const char *cstring);
JANET_API int32_t janet_sorted_keys(const JanetKV *dict, int32_t cap, int32_t *index_buffer);
/* VM functions */
JANET_API int janet_init(void);
@@ -1843,7 +1842,7 @@ typedef enum {
RULE_READINT, /* [(signedness << 4) | (endianess << 5) | bytewidth, tag] */
RULE_LINE, /* [tag] */
RULE_COLUMN, /* [tag] */
RULE_UNREF /* [rule, tag] */
RULE_MATCHCAP /* [rule_a, rule_b] */
} JanetPegOpcod;
typedef struct {

View File

@@ -481,16 +481,13 @@
(check-deep '(to "b") "aaaa" nil)
(check-deep '(thru "b") "aaaa" nil)
# unref
(def grammar
(peg/compile
~{:main (* :tagged -1)
:tagged (unref (replace (* :open-tag :value :close-tag) ,struct))
:open-tag (* (constant :tag) "<" (capture :w+ :tag-name) ">")
:value (* (constant :value) (group (any (+ :tagged :untagged))))
:close-tag (* "</" (backmatch :tag-name) ">")
:untagged (capture (any (if-not "<" 1)))}))
(check-deep grammar "<p><em>foobar</em></p>" @[{:tag "p" :value @[{:tag "em" :value @["foobar"]}]}])
(check-deep grammar "<p>foobar</p>" @[{:tag "p" :value @["foobar"]}])
# matchcap
(def matchcap-test
~{:span (* "{" '(to "}") "}")
:parse-span (* '3 3)
:main (matchcap :parse-span (any :span ))})
(check-deep matchcap-test "{bigrig}{fatman}{catdog}" @["big" "fat" "cat"])
(check-deep matchcap-test "{bigrig}{fatman}{catdoggy}" @["big" "fat" "cat"])
(end-suite)