1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-24 17:27:18 +00:00

Merge remote-tracking branch 'pyrmont/feature.markdown-docstrings' into longstring-autoindent

This commit is contained in:
Calvin Rose 2020-11-27 12:13:45 -06:00
commit fb0859dfe6

View File

@ -12,7 +12,7 @@
(def defn :macro
```
(defn name & more)
Define a function. Equivalent to (def name (fn name [args] ...)).
```
(fn defn [name & more]
@ -119,7 +119,7 @@
(def idempotent?
```
(idempotent? x)
Check if x is a value that evaluates to itself when compiled.
```
(do
@ -544,41 +544,52 @@
either bindings or conditionals. A binding is a sequence of three values
that define something to loop over. They are formatted like:
binding :verb object/expression
binding :verb object/expression
Where binding is a binding as passed to def, :verb is one of a set of keywords,
and object is any expression. The available verbs are:
Where `binding` is a binding as passed to def, `:verb` is one of a set of
keywords, and `object` is any expression. The available verbs are:
* :iterate - repeatedly evaluate and bind to the expression while it is truthy.
* :range - loop over a range. The object should be a two-element tuple with a start
and end value, and an optional positive step. The range is half open, [start, end).
* :range-to - same as :range, but the range is inclusive [start, end].
* :down - loop over a range, stepping downwards. The object should be a two-element tuple
with a start and (exclusive) end value, and an optional (positive!) step size.
* :down-to - same :as down, but the range is inclusive [start, end].
* :keys - iterate over the keys in a data structure.
* :pairs - iterate over the key-value pairs as tuples in a data structure.
* :in - iterate over the values in a data structure.
* :generate - iterate over values yielded from a fiber. Can be paired with the generator
function for the producer/consumer pattern.
* :iterate -- repeatedly evaluate and bind to the expression while it is
truthy.
loop also accepts conditionals to refine the looping further. Conditionals are of
* :range -- loop over a range. The object should be a two-element tuple with
a start and end value, and an optional positive step. The range is half
open, [start, end).
* :range-to -- same as :range, but the range is inclusive [start, end].
* :down -- loop over a range, stepping downwards. The object should be a
two-element tuple with a start and (exclusive) end value, and an optional
(positive!) step size.
* :down-to -- same :as down, but the range is inclusive [start, end].
* :keys -- terate over the keys in a data structure.
* :pairs -- iterate over the key-value pairs as tuples in a data structure.
* :in -- iterate over the values in a data structure.
* :generate -- iterate over values yielded from a fiber. Can be paired with
the generator function for the producer/consumer pattern.
`loop` also accepts conditionals to refine the looping further. Conditionals are of
the form:
:modifier argument
:modifier argument
where :modifier is one of a set of keywords, and argument is keyword-dependent.
:modifier can be one of:
where `:modifier` is one of a set of keywords, and `argument` is keyword-dependent.
`:modifier` can be one of:
* :while expression - breaks from the loop if expression is falsey.
* :until expression - breaks from the loop if expression is truthy.
* :let bindings - defines bindings inside the loop as passed to the let macro.
* :before form - evaluates a form for a side effect before the next inner loop.
* :after form - same as :before, but the side effect happens after the next inner loop.
* :repeat n - repeats the next inner loop n times.
* :when condition - only evaluates the loop body when condition is true.
* `:while expression` - breaks from the loop if `expression` is falsey.
* `:until expression` - breaks from the loop if `expression` is truthy.
* `:let bindings` - defines bindings inside the loop as passed to the `let` macro.
* `:before form` - evaluates a form for a side effect before the next inner loop.
* `:after form` - same as `:before`, but the side effect happens after the next inner loop.
* `:repeat n` - repeats the next inner loop `n` times.
* `:when condition` - only evaluates the loop body when condition is true.
The loop macro always evaluates to nil.
The `loop` macro always evaluates to nil.
```
[head & body]
(loop1 body head 0))
@ -1683,48 +1694,268 @@
(env-walk keyword? env local))
(defn doc-format
`Reformat text to wrap at a given line. By default, uses the value of
(dyn :doc-width 120) to wrap text. Returns a buffer containing a formatted
docstring.`
[text &opt width]
`Reformat a docstring to wrap a certain width.
Returns a buffer containing the formatted text.`
[str &opt width indent]
(default indent 4)
(def max-width (- (or width (dyn :doc-width 80)) 8))
(def len (length str))
(def res @"")
(var pos 0)
(def line @"")
(var line-width 0)
(def levels @[0])
(var leading 0)
(var c nil)
(def maxcol (- (or width (dyn :doc-width 120)) 8))
(var buf @" ")
(var word @"")
(var current 0)
(var indenting true)
(def base-indent
# Is there a better way?
(do
(var min-indent 0)
(var curr-indent 0)
(var start-of-line false)
(set c (get str pos))
(while (not= nil c)
(case c
10 (do
(set start-of-line true)
(set curr-indent 0))
32 (when start-of-line
(++ curr-indent))
(when start-of-line
(set start-of-line false)
(when (or (= 0 min-indent)
(< curr-indent min-indent))
(set min-indent curr-indent))))
(set c (get str (++ pos))))
min-indent))
(defn pushword
[]
(def oldcur current)
(def spacer
(if (<= maxcol (+ current (length word) 1))
(do (set current 0) (set indenting true) "\n ")
(do (++ current) " ")))
(+= current (length word))
(if (> oldcur 0)
(buffer/push-string buf spacer))
(buffer/push-string buf word)
(buffer/clear word))
(set pos 0)
(each b text
(if (and (not= b 10) (not= b 32))
(if (= b 9)
(buffer/push-string word " ")
(do (set indenting false) (buffer/push-byte word b)))
(if (and indenting (= b 32))
(buffer/push-byte word b)
(do
(if (> (length word) 0) (pushword))
(when (= b 10)
(buffer/push-string buf "\n ")
(set current 0)
(set indenting true))))))
(defn skip-base-indent []
(var pos* pos)
(set c (get str pos*))
(while (and (< (- pos* pos) base-indent)
(= 32 c))
(set c (get str (++ pos*))))
(set pos pos*))
# Last word
(pushword)
(defn skip-line-indent []
(var pos* pos)
(set c (get str pos*))
(while (and (not= nil c)
(not= 10 c)
(= 32 c))
(set c (get str (++ pos*))))
(set leading (- pos* pos))
(set pos pos*))
buf)
(defn update-levels []
(while (< leading (array/peek levels))
(array/pop levels)))
(defn start-nl? []
(= 10 (get str pos)))
(defn start-fcb? []
(and (= 96 (get str (+ pos)))
(= 96 (get str (+ pos 1)))
(= 96 (get str (+ pos 2)))))
(defn end-fcb? []
(and (= 96 (get str (+ pos)))
(= 96 (get str (+ pos 1)))
(= 96 (get str (+ pos 2)))
(= 10 (get str (+ pos 3)))))
(defn start-icb? []
(and (not= leading (array/peek levels))
(or (= 4 leading)
(= 4 (- leading (array/peek levels))))))
(defn start-ul? []
(var pos* pos)
(var c* (get str pos*))
(while (and (not= nil c*)
(= 32 c*))
(set c* (get str (++ pos*))))
(and (or (= 42 c*)
(= 43 c*)
(= 45 c*))
(= 32 (get str (+ pos* 1)))))
(defn start-ol? []
(var pos* pos)
(var c* (get str pos*))
(while (and (not= nil c*)
(= 32 c*))
(set c* (get str (++ pos*))))
(while (and (not= nil c*)
(<= 48 c*)
(>= 57 c*))
(set c* (get str (++ pos*))))
(set c* (get str (-- pos*)))
(and (<= 48 c*)
(>= 57 c*)
(= 46 (get str (+ pos* 1)))
(= 32 (get str (+ pos* 2)))))
(defn push-line []
(buffer/push-string res (buffer/new-filled indent 32))
(set c (get str pos))
(while (not= 10 c)
(buffer/push-byte res c)
(set c (get str (++ pos))))
(buffer/push-byte res c)
(++ pos))
(defn push-bullet []
(var pos* pos)
(buffer/push-string line (buffer/new-filled leading 32))
(set c (get str pos*))
# Add bullet
(while (and (not= nil c) (not= 32 c))
(buffer/push-byte line c)
(set c (get str (++ pos*))))
# Add item indentation
(while (and (not= nil c) (= 32 c))
(buffer/push-byte line c)
(set c (get str (++ pos*))))
# Record indentation if necessary
(def item-indent (+ leading (- pos* pos)))
(when (not= item-indent (array/peek levels))
(array/push levels item-indent))
# Update line width
(+= line-width item-indent)
# Update position
(set pos pos*))
(defn push-word [hang-indent]
(def word @"")
(var word-len 0)
# Build a word
(while (and (not= nil c)
(not= 10 c)
(not= 32 c))
(buffer/push-byte word c)
(++ word-len)
(set c (get str (++ pos))))
# Start new line if necessary
(when (> (+ line-width word-len) max-width)
# Push existing line
(buffer/push-byte line 10)
(buffer/push-string res line)
(buffer/clear line)
# Indent new line
(buffer/push-string line (buffer/new-filled hang-indent 32))
(set line-width hang-indent))
# Add single space if not beginning of line
(when (not= line-width hang-indent)
(buffer/push-byte line 32)
(++ line-width))
# Push word onto line
(buffer/push-string line word)
(set line-width (+ line-width word-len)))
(defn push-nl []
(when (< pos len)
(buffer/push-byte res 10)
(++ pos)))
(defn push-list []
(update-levels)
# Indent first line
(buffer/push-string line (buffer/new-filled indent 32))
(set line-width indent)
# Add bullet
(push-bullet)
# Add words
(set c (get str pos))
(while (and (not= nil c)
(not= 10 c))
# Skip spaces
(while (= 32 c)
(set c (get str (++ pos))))
# Add word
(push-word (+ indent (array/peek levels)))
(def old-c c)
(set c (get str (++ pos)))
# Check if next line is a new item
(when (and (= 10 old-c)
(or (start-ul?)
(start-ol?)))
(set c (get str (-- pos)))))
# Add final line
(buffer/push-string res line)
(buffer/clear line)
# Move position back for newline
(-- pos)
(push-nl))
(defn push-fcb []
(update-levels)
(push-line)
(skip-base-indent)
(while (not (end-fcb?))
(push-line)
(skip-base-indent))
(push-line))
(defn push-icb []
(buffer/push-string res (buffer/new-filled leading 32))
(push-line)
(skip-base-indent)
(while (not (start-nl?))
(push-line)
(skip-base-indent))
(push-nl))
(defn push-p []
(update-levels)
# Set up the indentation
(def para-indent (+ indent (array/peek levels)))
# Indent first line
(buffer/push-string line (buffer/new-filled para-indent 32))
(set line-width para-indent)
# Add words
(set c (get str pos))
(while (and (not= nil c)
(not= 10 c))
# Skip spaces
(while (= 32 c)
(set c (get str (++ pos))))
# Add word
(push-word para-indent)
(set c (get str (++ pos))))
# Add final line
(buffer/push-string res line)
(buffer/clear line)
# Move position back for newline
(-- pos)
(push-nl)
(push-nl))
(while (< pos len)
(skip-base-indent)
(skip-line-indent)
(cond
(start-nl?)
(push-nl)
(start-ul?)
(push-list)
(start-ol?)
(push-list)
(start-fcb?)
(push-fcb)
(start-icb?)
(push-icb)
(push-p)))
res)
(defn- print-index
"Print bindings in the current environment given a filter function"
@ -2348,7 +2579,7 @@
Each element is a two-element tuple, containing the path
template and a keyword :source, :native, or :image indicating how
require should load files found at these paths.
A tuple can also
contain a third element, specifying a filter that prevents module/find
from searching that path template if the filter doesn't match the input
@ -2531,10 +2762,10 @@
(require-1 path args (struct ;args)))
(defn merge-module
"Merge a module source into the target environment with a prefix, as with the import macro.
`Merge a module source into the target environment with a prefix, as with the import macro.
This lets users emulate the behavior of import with a custom module table.
If export is truthy, then merged functions are not marked as private. Returns
the modified target environment."
the modified target environment.`
[target source &opt prefix export]
(loop [[k v] :pairs source :when (symbol? k) :when (not (v :private))]
(def newv (table/setproto @{:private (not export)} v))