about summary refs log tree commit diff stats
path: root/doc
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2019-05-17 13:45:20 -0400
committerDrew DeVault <sir@cmpwn.com>2019-05-17 13:46:26 -0400
commitfc719e47c452564b56d2ed0b7828fc74e72fcef8 (patch)
treef392219dbc10ce7690142927d4215ab88e71275d /doc
parentfcdcd32de7118e1de1f8d94c380a0e80a64265ec (diff)
downloadaerc-fc719e47c452564b56d2ed0b7828fc74e72fcef8.tar.gz
Add man pages
Diffstat (limited to 'doc')
-rw-r--r--doc/aerc-config.5.scd319
-rw-r--r--doc/aerc-imap.5.scd46
-rw-r--r--doc/aerc-smtp.5.scd50
-rw-r--r--doc/aerc.1.scd103
4 files changed, 518 insertions, 0 deletions
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
new file mode 100644
index 0000000..a221723
--- /dev/null
+++ b/doc/aerc-config.5.scd
@@ -0,0 +1,319 @@
+aerc-config(5)
+
+# NAME
+
+aerc-config - configuration file formats for *aerc*(1)
+
+# CONFIGURATION
+
+There are three aerc config files: *aerc.conf*, *binds.conf*, and
+*accounts.conf*. The last one must be kept secret, as it may include your
+account credentials. We look for these files in your XDG config home plus
+"aerc", which defaults to ~/.config/aerc.
+
+Examples of these config files are typically included with your installation of
+aerc and are usually installed in /usr/share/aerc.
+
+Each file uses the _ini_ format, and consists of sections with keys and values.
+A line beginning with # is considered a comment and ignored, as are empty lines.
+New sections begin with [section-name] on a single line, and keys and values are
+separated with "=".
+
+# AERC.CONF
+
+This file is used for configuring the general appearance and behavior of aerc.
+
+## UI OPTIONS
+
+These options are configured in the *[ui]* section of aerc.conf.
+
+*index-format*
+	Describes the format for each row in a mailbox view. This field is
+	compatible with mutt's printf-like syntax. TODO: document properly
+
+	Default: %4C %Z %D %-17.17n %s
+
+*timestamp-format*
+	See strftime(3)
+
+	Default: %F %l:%M %p (ISO 8501 + 12 hour time)
+
+*sidebar-width*
+	Width of the sidebar, including the border. Set to zero to disable the
+	sidebar.
+
+	Default: 20
+
+*empty-message*
+	Message to display when viewing an empty folder.
+
+	Default: (no messages)
+
+## VIEWER
+
+These options are configured in the *[viewer]* section of aerc.conf.
+
+*pager*
+	Specifies the pager to use when displaying emails. Note that some filters
+	may add ANSI escape sequences to add color to rendered emails, so you may
+	want to use a pager which supports ANSI.
+
+	Default: less -R
+
+*alternatives*
+	If an email offers several versions (multipart), you can configure which
+	mimetype to prefer. For example, this can be used to prefer plaintext over
+	HTML emails.
+
+	Default: text/plain,text/html
+
+## COMPOSE
+
+These options are configured in the *[viewer]* section of aerc.conf.
+
+*editor*
+	Specifies the command to run the editor with. It will be shown in an
+	embedded terminal, though it may also launch a graphical window if the
+	environment supports it. Defaults to *$EDITOR*, or *vi*(1).
+
+## FILTERS
+
+Filters allow you to pipe an email body through a shell command to render
+certain emails differently, e.g. highlighting them with ANSI escape codes.
+They are configured in the *[filters]* section of aerc.conf.
+
+The first filter which matches the email's mimetype will be used, so order
+them from most to least specific.
+
+You can also match on non-mimetypes, by prefixing with the header to match
+against (non-case-sensitive) and a comma, e.g. subject,text will match a
+subject which contains "text". Use header,~regex to match against a regex.
+
+Most aerc installations come with some useful filters, typically installed in
+/usr/share/aerc/filters. Here is an example config which uses these filters:
+
+```
+subject,~^\[PATCH=/usr/share/aerc/filters/hldiff.py
+text/html=/usr/share/aerc/filters/html
+text/*=/usr/share/aerc/filters/plaintext.py
+```
+
+Note that the filters which are installed with aerc have additional
+dependencies, such as Python, sockify, and w3m.
+
+# ACCOUNTS.CONF
+
+This file is used for configuring each mail account used for aerc. Each section
+is the name of an account you want to configure, and the keys & values in that
+section specify details of that account's configuration. In addition to the
+options documented here, specific transports for incoming and outgoing emails
+may have additional configuration parameters, documented on their respective man
+pages.
+
+Note that many of these configuration options are written for you, such as
+*source* and *outgoing*, when you run the account configuration wizard
+(*:new-account*).
+
+*copy-to*
+	Specifies a folder to copy sent mails to, usually "Sent".
+
+	Default: none
+
+*default*
+	Specifies the default folder to open in the message list when aerc
+	configures this account.
+
+	Default: INBOX
+
+*folders*
+	Specifies the list of folders to display in the sidebar.
+
+	Default: all folders
+
+*from*
+	The default value to use for the From header in new emails. This should be
+	an RFC 5322-compatible string, such as "Your Name <you@example.org>".
+
+	Default: none
+
+*outgoing*
+	Specifies the transport for sending outgoing emails on this account.  It
+	should be a connection string, and the specific meaning of each component
+	varies depending on the protocol in use.  See each protocol's man page for
+	more details:
+
+	- *aerc-smtp*(5)
+
+*source*
+	Specifies the source for reading incoming emails on this account. This key
+	is required for all accounts. It should be a connection string, and the
+	specific meaning of each component varies depending on the protocol in use.
+	See each protocol's man page for more details:
+
+	- *aerc-imap*(5)
+
+	Default: none
+
+# BINDS.CONF
+
+This file is used for configuring keybindings used in the aerc interactive
+client. You may configure different keybindings for different contexts by
+writing them into different *[sections]* of the ini file. The available contexts
+are:
+
+*[messages]*
+	keybindings for the message list
+
+*[view]*
+	keybindings for the message viewer
+
+*[compose]*
+	keybindings for the message composer
+
+*[compose::editor]*
+	keybindings for the composer, when the editor is focused
+
+*[compose::review]*
+	keybindings for the composer, when reviewing the email before it's sent
+
+*[terminal]*
+	keybindings for terminal tabs
+
+You may also configure global keybindings by placing them at the beginning of
+the file, before specifying any context-specific sections. For each *key=value*
+option specified, the _key_ is the keystrokes pressed (in order) to invoke this
+keybinding, and _value_ specifies keystrokes that aerc will simulate when the
+keybinding is invoked. Generally this is used to execute commands, for example:
+
+	rq = :reply -q<Enter>
+
+Pressing r, then q, will simulate typing in ":reply -q<Enter>", and execute
+:reply -q accordingly. It is also possible to invoke keybindings recursively in
+a similar fashion. Additionally, the following special options are available in
+each binding context:
+
+*$noinherit*
+	If set to "true", global keybindings will not be effective in this context.
+
+	Default: false
+
+*$ex*
+	This can be set to a keystroke which will bring up the command input in this
+	context.
+	
+	Default: <semicolon>
+
+In addition to letters, special keys may be specified in <angle brackets>. The
+following special keys are supported:
+
+[[ *Name*
+:- *Description*
+|  space
+:  " "
+|  semicolon
+:  ;
+|  tab
+:  
+|  enter
+:  
+|  up
+:  
+|  down
+:  
+|  right
+:  
+|  left
+:  
+|  pgup
+:  
+|  pgdn
+:  
+|  home
+:  
+|  end
+:  
+|  insert
+:  
+|  delete
+:  
+|  exit
+:  
+|  cancel
+:  
+|  print
+:  
+|  pause
+:  
+|  backtab
+|  c-space
+:  Ctrl+Space
+|  c-a
+:  Ctrl+a
+|  c-b
+:  Ctrl+b
+|  c-c
+:  Ctrl+c
+|  c-d
+:  Ctrl+d
+|  c-e
+:  Ctrl+e
+|  c-f
+:  Ctrl+f
+|  c-g
+:  Ctrl+g
+|  c-h
+:  Ctrl+h
+|  c-i
+:  Ctrl+i
+|  c-j
+:  Ctrl+j
+|  c-k
+:  Ctrl+k
+|  c-l
+:  Ctrl+l
+|  c-m
+:  Ctrl+m
+|  c-n
+:  Ctrl+n
+|  c-o
+:  Ctrl+o
+|  c-p
+:  Ctrl+p
+|  c-q
+:  Ctrl+q
+|  c-r
+:  Ctrl+r
+|  c-s
+:  Ctrl+s
+|  c-t
+:  Ctrl+t
+|  c-u
+:  Ctrl+u
+|  c-v
+:  Ctrl+v
+|  c-w
+:  Ctrl+w
+|  c-x
+:  Ctrl+x
+|  c-y
+:  Ctrl+y
+|  c-z
+:  Ctrl+z
+|  c-]
+:  Ctrl+]
+|  c-[
+:  Ctrl+[
+|  c-^
+:  Ctrl+^
+|  c-_
+:  Ctrl+_
+
+# SEE ALSO
+
+*aerc*(1) *aerc-imap*(5) *aerc-smtp*(5)
+
+# AUTHORS
+
+Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
+source contributors. For more information about aerc development, see
+https://git.sr.ht/~sircmpwn/aerc.
diff --git a/doc/aerc-imap.5.scd b/doc/aerc-imap.5.scd
new file mode 100644
index 0000000..5899a34
--- /dev/null
+++ b/doc/aerc-imap.5.scd
@@ -0,0 +1,46 @@
+aerc-imap(5)
+
+# NAME
+
+aerc-imap - IMAP configuration for *aerc*(1)
+
+# SYNOPSIS
+
+aerc implements the IMAP protocol as specified by RFC 3501, with the following
+IMAP extensions:
+
+- IDLE (RFC 2177)
+
+# CONFIGURATION
+
+IMAP configuration may be done interactively with the :new-account command.
+
+In accounts.conf (see *aerc-config*(5)), the following IMAP-specific options are
+available:
+
+*source*
+	imap[s][+insecure]://username[:password]@hostname[:port]
+
+	Remember that all fields must be URL encoded. The "@" symbol, when URL
+	encoded, is *%40*.
+
+	The meaning of the scheme component is:
+
+	*imap://*:
+		IMAP with STARTTLS
+
+	*imap+insecure://*:
+		IMAP without STARTTLS
+
+	*imaps*:
+		IMAP with TLS/SSL
+
+# SEE ALSO
+
+*aerc*(1) *aerc-config*(5) *aerc-smtp*(5)
+
+# AUTHORS
+
+Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
+source contributors. For more information about aerc development, see
+https://git.sr.ht/~sircmpwn/aerc.
diff --git a/doc/aerc-smtp.5.scd b/doc/aerc-smtp.5.scd
new file mode 100644
index 0000000..7d07125
--- /dev/null
+++ b/doc/aerc-smtp.5.scd
@@ -0,0 +1,50 @@
+aerc-smtp(5)
+
+# NAME
+
+aerc-smtp - SMTP configuration for *aerc*(1)
+
+# SYNOPSIS
+
+aerc implements the SMTP protocol as specified by RFC 5321.
+
+# CONFIGURATION
+
+SMTP configuration may be done interactively with the :new-account command.
+
+In accounts.conf (see *aerc-config*(5)), the following SMTP-specific options are
+available:
+
+*outgoing*
+	smtp[s][+plain|+none]://username[:password]@hostname[:port]
+
+	Remember that all fields must be URL encoded. The "@" symbol, when URL
+	encoded, is *%40*.
+
+	The meaning of the scheme component is:
+
+	*smtp://*:
+		Unencrypted SMTP
+
+	*smtps://*:
+		SMTP with TLS/SSL
+
+	Additionally, you can specify an authentication mechansim like so:
+
+	*+none*:
+		No authentication is required to use this SMTP server. You may omit the
+		username and password in this case.
+
+	*+plain*:
+		Authenticate with a username and password using AUTH PLAIN. This is the
+		default behavior.
+
+# SEE ALSO
+
+*aerc*(1) *aerc-config*(5) *aerc-smtp*(5)
+
+# AUTHORS
+
+Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
+source contributors. For more information about aerc development, see
+https://git.sr.ht/~sircmpwn/aerc.
diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd
new file mode 100644
index 0000000..5d501f0
--- /dev/null
+++ b/doc/aerc.1.scd
@@ -0,0 +1,103 @@
+aerc(1)
+
+# NAME
+
+aerc - the world's best email client
+
+# SYNOPSIS
+
+_aerc_
+
+Starts the interactive aerc mail client on /dev/tty.
+
+# RUNTIME COMMANDS
+
+To execute a command, press : to summon the command interface. Commands may also
+be bound to keys, see *aerc-config*(5) for details.
+
+Different commands work in different contexts, depending on the kind of tab you
+have selected.
+
+## GLOBAL COMMANDS
+
+These commands work in any context.
+
+*cd* <directory>
+	Changes aerc's current working directory.
+
+*term* [command...]
+	Opens a new terminal tab with a shell running in the current working
+	directory, or the specified command.
+
+*prev-tab* [n], *next-tab* [n]
+	Cycles to the previous or next tab in the list, repeating n times
+	(default: 1).
+
+*quit*
+	Exits aerc.
+
+## MESSAGE LIST COMMANDS
+
+*cf* <folder>
+	Change the folder shown in the message list.
+
+*compose*
+	Open the compose window to send a new email. The new email will be sent with
+	the current account's outgoing transport configuration, see
+	*aerc-config*(5) for details on configuring outgoing emails.
+
+*copy* <target>
+	Copies the selected message to the target folder.
+
+*delete-message*
+	Deletes the selected message.
+
+*move* <target>
+	Moves the selected message to the target folder.
+
+*next-folder* <n>, *prev-folder* <n>
+	Cycles to the next (or previous) folder shown in the sidebar, repeated n
+	times (default: 1).
+
+*next-message* <n>[%], *prev-message* <n>[%]
+	Selects the next (or previous) message in the message list. If specified as
+	a percentage, the percentage is applied to the number of messages shown on
+	screen and the cursor advances that far.
+
+*pipe* <cmd>
+	Downloads and pipes the selected message into the given shell command, and
+	opens a new terminal tab to show the result.
+
+*reply* [-aq]
+	Opens the composer to reply to the selected message.
+
+	*-a*: Reply all
+
+	*-q*: Insert a quoted version of the selected message into the reply editor
+
+*select-message* <n>
+	Selects the nth message in the message list (and scrolls it into view if
+	necessary).
+
+*view-message*
+	Opens the message viewer to display the selected message.
+
+## MESSAGE VIEW COMMANDS
+
+*close*
+	Closes the message viewer.
+
+## TERMINAL COMMANDS
+
+*close*
+	Closes the terminal.
+
+# SEE ALSO
+
+*aerc-config*(5) *aerc-imap*(5) *aerc-smtp*(5)
+
+# AUTHORS
+
+Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open
+source contributors. For more information about aerc development, see
+https://git.sr.ht/~sircmpwn/aerc.
lt;- to-mu cells ] # 'parse' will turn lambda expressions into trees made of cells exclusive-container cell [ atom:text pair:pair ] # printed below as < first | rest > container pair [ first:&:cell rest:&:cell ] def new-atom name:text -> result:&:cell [ local-scope load-inputs result <- new cell:type *result <- merge 0/tag:atom, name ] def new-pair a:&:cell, b:&:cell -> result:&:cell [ local-scope load-inputs result <- new cell:type *result <- merge 1/tag:pair, a/first, b/rest ] def is-atom? x:&:cell -> result:bool [ local-scope load-inputs return-unless x, false _, result <- maybe-convert *x, atom:variant ] def is-pair? x:&:cell -> result:bool [ local-scope load-inputs return-unless x, false _, result <- maybe-convert *x, pair:variant ] scenario atom-is-not-pair [ local-scope s:text <- new [a] x:&:cell <- new-atom s 10:bool/raw <- is-atom? x 11:bool/raw <- is-pair? x memory-should-contain [ 10 <- 1 11 <- 0 ] ] scenario pair-is-not-atom [ local-scope # construct (a . nil) s:text <- new [a] x:&:cell <- new-atom s y:&:cell <- new-pair x, null 10:bool/raw <- is-atom? y 11:bool/raw <- is-pair? y memory-should-contain [ 10 <- 0 11 <- 1 ] ] def atom-match? x:&:cell, pat:text -> result:bool [ local-scope load-inputs s:text, is-atom?:bool <- maybe-convert *x, atom:variant return-unless is-atom?, false result <- equal pat, s ] scenario atom-match [ local-scope x:&:cell <- new-atom [abc] 10:bool/raw <- atom-match? x, [abc] memory-should-contain [ 10 <- 1 ] ] def first x:&:cell -> result:&:cell [ local-scope load-inputs pair:pair, pair?:bool <- maybe-convert *x, pair:variant return-unless pair?, null result <- get pair, first:offset ] def rest x:&:cell -> result:&:cell [ local-scope load-inputs pair:pair, pair?:bool <- maybe-convert *x, pair:variant return-unless pair?, null result <- get pair, rest:offset ] def set-first base:&:cell, new-first:&:cell -> base:&:cell [ local-scope load-inputs pair:pair, is-pair?:bool <- maybe-convert *base, pair:variant return-unless is-pair? pair <- put pair, first:offset, new-first *base <- merge 1/pair, pair ] def set-rest base:&:cell, new-rest:&:cell -> base:&:cell [ local-scope load-inputs pair:pair, is-pair?:bool <- maybe-convert *base, pair:variant return-unless is-pair? pair <- put pair, rest:offset, new-rest *base <- merge 1/pair, pair ] scenario cell-operations-on-atom [ local-scope s:text <- new [a] x:&:cell <- new-atom s 10:&:cell/raw <- first x 11:&:cell/raw <- rest x memory-should-contain [ 10 <- 0 # first is nil 11 <- 0 # rest is nil ] ] scenario cell-operations-on-pair [ local-scope # construct (a . nil) s:text <- new [a] x:&:cell <- new-atom s y:&:cell <- new-pair x, null x2:&:cell <- first y 10:bool/raw <- equal x, x2 11:&:cell/raw <- rest y memory-should-contain [ 10 <- 1 # first is correct 11 <- 0 # rest is nil ] ] ## convert lambda text to a tree of cells def parse in:text -> out:&:cell [ local-scope load-inputs s:&:stream:char <- new-stream in out, s <- parse s trace 2, [app/parse], out ] def parse in:&:stream:char -> out:&:cell, in:&:stream:char [ local-scope load-inputs # skip whitespace in <- skip-whitespace in c:char, eof?:bool <- peek in return-if eof?, null pair?:bool <- equal c, 40/open-paren { break-if pair? # atom buf:&:buffer:char <- new-buffer 30 { done?:bool <- end-of-stream? in break-if done? # stop before close paren or space c:char <- peek in done? <- equal c, 41/close-paren break-if done? done? <- space? c break-if done? c <- read in buf <- append buf, c loop } s:text <- buffer-to-array buf out <- new-atom s } { break-unless pair? # pair read in # skip the open-paren out <- new cell:type # start out with nil # read in first element of pair { end?:bool <- end-of-stream? in not-end?:bool <- not end? assert not-end?, [unbalanced '(' in expression] c <- peek in close-paren?:bool <- equal c, 41/close-paren break-if close-paren? first:&:cell, in <- parse in *out <- merge 1/pair, first, null } # read in any remaining elements curr:&:cell <- copy out { in <- skip-whitespace in end?:bool <- end-of-stream? in not-end?:bool <- not end? assert not-end?, [unbalanced '(' in expression] # termination check: ')' c <- peek in { close-paren?:bool <- equal c, 41/close-paren break-unless close-paren? read in # skip ')' break +end-pair } # still here? read next element of pair next:&:cell, in <- parse in is-dot?:bool <- atom-match? next, [.] { break-if is-dot? next-curr:&:cell <- new-pair next, null curr <- set-rest curr, next-curr curr <- rest curr } { break-unless is-dot? # deal with dotted pair in <- skip-whitespace in c <- peek in not-close-paren?:bool <- not-equal c, 41/close-paren assert not-close-paren?, [')' cannot immediately follow '.'] final:&:cell <- parse in curr <- set-rest curr, final # we're not gonna update curr, so better make sure the next iteration # is going to end the pair in <- skip-whitespace in c <- peek in close-paren?:bool <- equal c, 41/close-paren assert close-paren?, ['.' must be followed by exactly one expression before ')'] } loop } +end-pair } ] def skip-whitespace in:&:stream:char -> in:&:stream:char [ local-scope load-inputs { done?:bool <- end-of-stream? in return-if done?, null c:char <- peek in space?:bool <- space? c break-unless space? read in # skip loop } ] def to-text x:&:cell -> out:text [ local-scope load-inputs buf:&:buffer:char <- new-buffer 30 buf <- to-buffer x, buf out <- buffer-to-array buf ] def to-buffer x:&:cell, buf:&:buffer:char -> buf:&:buffer:char [ local-scope load-inputs # base case: empty cell { break-if x buf <- append buf, [<>] return } # base case: atom { s:text, atom?:bool <- maybe-convert *x, atom:variant break-unless atom? buf <- append buf, s return } # recursive case: pair buf <- append buf, [< ] first:&:cell <- first x buf <- to-buffer first, buf buf <- append buf, [ | ] rest:&:cell <- rest x buf <- to-buffer rest, buf buf <- append buf, [ >] ] scenario parse-single-letter-atom [ local-scope s:text <- new [a] x:&:cell <- parse s s2:text, 10:bool/raw <- maybe-convert *x, atom:variant 11:@:char/raw <- copy *s2 memory-should-contain [ 10 <- 1 # parse result is an atom 11:array:character <- [a] ] ] scenario parse-atom [ local-scope s:text <- new [abc] x:&:cell <- parse s s2:text, 10:bool/raw <- maybe-convert *x, atom:variant 11:@:char/raw <- copy *s2 memory-should-contain [ 10 <- 1 # parse result is an atom 11:array:character <- [abc] ] ] scenario parse-list-of-two-atoms [ local-scope s:text <- new [(abc def)] x:&:cell <- parse s trace-should-contain [ app/parse: < abc | < def | <> > > ] 10:bool/raw <- is-pair? x x1:&:cell <- first x x2:&:cell <- rest x s1:text, 11:bool/raw <- maybe-convert *x1, atom:variant 12:bool/raw <- is-pair? x2 x3:&:cell <- first x2 s2:text, 13:bool/raw <- maybe-convert *x3, atom:variant 14:&:cell/raw <- rest x2 20:@:char/raw <- copy *s1 30:@:char/raw <- copy *s2 memory-should-contain [ 10 <- 1 # parse result is a pair 11 <- 1 # result.first is an atom 12 <- 1 # result.rest is a pair 13 <- 1 # result.rest.first is an atom 14 <- 0 # result.rest.rest is nil 20:array:character <- [abc] # result.first 30:array:character <- [def] # result.rest.first ] ] scenario parse-list-with-extra-spaces [ local-scope s:text <- new [ ( abc def ) ] # extra spaces x:&:cell <- parse s trace-should-contain [ app/parse: < abc | < def | <> > > ] 10:bool/raw <- is-pair? x x1:&:cell <- first x x2:&:cell <- rest x s1:text, 11:bool/raw <- maybe-convert *x1, atom:variant 12:bool/raw <- is-pair? x2 x3:&:cell <- first x2 s2:text, 13:bool/raw <- maybe-convert *x3, atom:variant 14:&:cell/raw <- rest x2 20:@:char/raw <- copy *s1 30:@:char/raw <- copy *s2 memory-should-contain [ 10 <- 1 # parse result is a pair 11 <- 1 # result.first is an atom 12 <- 1 # result.rest is a pair 13 <- 1 # result.rest.first is an atom 14 <- 0 # result.rest.rest is nil 20:array:character <- [abc] # result.first 30:array:character <- [def] # result.rest.first ] ] scenario parse-list-of-more-than-two-atoms [ local-scope s:text <- new [(abc def ghi)] x:&:cell <- parse s trace-should-contain [ app/parse: < abc | < def | < ghi | <> > > > ] 10:bool/raw <- is-pair? x x1:&:cell <- first x x2:&:cell <- rest x s1:text, 11:bool/raw <- maybe-convert *x1, atom:variant 12:bool/raw <- is-pair? x2 x3:&:cell <- first x2 s2:text, 13:bool/raw <- maybe-convert *x3, atom:variant x4:&:cell <- rest x2 14:bool/raw <- is-pair? x4 x5:&:cell <- first x4 s3:text, 15:bool/raw <- maybe-convert *x5, atom:variant 16:&:cell/raw <- rest x4 20:@:char/raw <- copy *s1 30:@:char/raw <- copy *s2 40:@:char/raw <- copy *s3 memory-should-contain [ 10 <- 1 # parse result is a pair 11 <- 1 # result.first is an atom 12 <- 1 # result.rest is a pair 13 <- 1 # result.rest.first is an atom 14 <- 1 # result.rest.rest is a pair 15 <- 1 # result.rest.rest.first is an atom 16 <- 0 # result.rest.rest.rest is nil 20:array:character <- [abc] # result.first 30:array:character <- [def] # result.rest.first 40:array:character <- [ghi] # result.rest.rest ] ] scenario parse-nested-list [ local-scope s:text <- new [((abc))] x:&:cell <- parse s trace-should-contain [ app/parse: < < abc | <> > | <> > ] 10:bool/raw <- is-pair? x x1:&:cell <- first x 11:bool/raw <- is-pair? x x2:&:cell <- first x1 s1:text, 12:bool/raw <- maybe-convert *x2, atom:variant 13:&:cell/raw <- rest x1 14:&:cell/raw <- rest x 20:@:char/raw <- copy *s1 memory-should-contain [ 10 <- 1 # parse result is a pair 11 <- 1 # result.first is a pair 12 <- 1 # result.first.first is an atom 13 <- 0 # result.first.rest is nil 14 <- 0 # result.rest is nil 20:array:character <- [abc] # result.first.first ] ] scenario parse-nested-list-2 [ local-scope s:text <- new [((abc) def)] x:&:cell <- parse s trace-should-contain [ app/parse: < < abc | <> > | < def | <> > > ] 10:bool/raw <- is-pair? x x1:&:cell <- first x 11:bool/raw <- is-pair? x x2:&:cell <- first x1 s1:text, 12:bool/raw <- maybe-convert *x2, atom:variant 13:&:cell/raw <- rest x1 x3:&:cell <- rest x x4:&:cell <- first x3 s2:text, 14:bool/raw <- maybe-convert *x4, atom:variant 15:&:cell/raw <- rest x3 20:@:char/raw <- copy *s1 30:@:char/raw <- copy *s2 memory-should-contain [ 10 <- 1 # parse result is a pair 11 <- 1 # result.first is a pair 12 <- 1 # result.first.first is an atom 13 <- 0 # result.first.rest is nil 14 <- 1 # result.rest.first is an atom 15 <- 0 # result.rest.rest is nil 20:array:character <- [abc] # result.first.first 30:array:character <- [def] # result.rest.first ] ] # todo: uncomment these tests after we figure out how to continue tests after # assertion failures #? scenario parse-error [ #? local-scope #? s:text <- new [(] #? #? hide-errors #? x:&:cell <- parse s #? #? show-errors #? trace-should-contain [ #? error: unbalanced '(' in expression #? ] #? ] #? #? scenario parse-error-after-element [ #? local-scope #? s:text <- new [(abc] #? #? hide-errors #? x:&:cell <- parse s #? #? show-errors #? trace-should-contain [ #? error: unbalanced '(' in expression #? ] #? ] scenario parse-dotted-list-of-two-atoms [ local-scope s:text <- new [(abc . def)] x:&:cell <- parse s trace-should-contain [ app/parse: < abc | def > ] 10:bool/raw <- is-pair? x x1:&:cell <- first x x2:&:cell <- rest x s1:text, 11:bool/raw <- maybe-convert *x1, atom:variant s2:text, 12:bool/raw <- maybe-convert *x2, atom:variant 20:@:char/raw <- copy *s1 30:@:char/raw <- copy *s2 memory-should-contain [ # parses to < abc | def > 10 <- 1 # parse result is a pair 11 <- 1 # result.first is an atom 12 <- 1 # result.rest is an atom 20:array:character <- [abc] # result.first 30:array:character <- [def] # result.rest ] ] scenario parse-dotted-list-of-more-than-two-atoms [ local-scope s:text <- new [(abc def . ghi)] x:&:cell <- parse s trace-should-contain [ app/parse: < abc | < def | ghi > > ] 10:bool/raw <- is-pair? x x1:&:cell <- first x x2:&:cell <- rest x s1:text, 11:bool/raw <- maybe-convert *x1, atom:variant 12:bool/raw <- is-pair? x2 x3:&:cell <- first x2 s2:text, 13:bool/raw <- maybe-convert *x3, atom:variant x4:&:cell <- rest x2 s3:text, 14:bool/raw <- maybe-convert *x4, atom:variant 20:@:char/raw <- copy *s1 30:@:char/raw <- copy *s2 40:@:char/raw <- copy *s3 memory-should-contain [ 10 <- 1 # parse result is a pair 11 <- 1 # result.first is an atom 12 <- 1 # result.rest is a pair 13 <- 1 # result.rest.first is an atom 14 <- 1 # result.rest.rest is an atom 20:array:character <- [abc] # result.first 30:array:character <- [def] # result.rest.first 40:array:character <- [ghi] # result.rest.rest ] ] ## convert tree of cells to Mu text def to-mu in:&:cell -> out:text [ local-scope load-inputs buf:&:buffer:char <- new-buffer 30 buf <- to-mu in, buf out <- buffer-to-array buf ] def to-mu in:&:cell, buf:&:buffer:char -> buf:&:buffer:char, result-name:text [ local-scope load-inputs # null cell? no change. # pair with all atoms? gensym a new variable # pair containing other pairs? recurse result-name <- copy null ]