beowulf.bootstrap
Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual
; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host
, be sufficient to bootstrap the full Lisp 1.5 interpreter..
Generated by Codox
Beowulf 0.3.0
beowulf.bootstrap
Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual
; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host
, be sufficient to bootstrap the full Lisp 1.5 interpreter..
The convention is adopted that functions in this file with names in ALLUPPERCASE are Lisp 1.5 functions (although written in Clojure) and that therefore all arguments must be numbers, symbols or beowulf.cons_cell.ConsCell
objects.
APPLY
(APPLY function args environment depth)
Apply this function
to these arguments
in this environment
and return the result.
For bootstrapping, at least, a version of APPLY written in Clojure. All args are assumed to be symbols or beowulf.cons-cell/ConsCell
objects. See page 13 of the Lisp 1.5 Programmers Manual.
EVAL
(EVAL expr)
(EVAL expr env depth)
Evaluate this expr
and return the result. If environment
is not passed, it defaults to the current value of the global object list. The depth
argument is part of the tracing system and should not be set by user code.
All args are assumed to be numbers, symbols or beowulf.cons-cell/ConsCell
objects. However, if called with just a single arg, expr
, I’ll assume it’s being called from the Clojure REPL and will coerce the expr
to ConsCell
.
PROG
(PROG program env depth)
The accursed PROG
feature. See page 71 of the manual.
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.cons-cell
The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, and must have both CAR and CDR mutable, so cannot be implemented on top of Clojure lists.
F
The canonical false value - different from NIL
, which is not canonically false in Lisp 1.5.
make-beowulf-list
(make-beowulf-list x)
Construct a linked list of cons cells with the same content as the sequence x
.
make-cons-cell
(make-cons-cell car cdr)
Construct a new instance of cons cell with this car
and cdr
.
MutableSequence
protocol
Like a sequence, but mutable.
members
getCar
(getCar this)
Return the first element of this sequence.
getCdr
(getCdr this)
like more
, q.v., but returns List NIL
not Clojure nil
when empty.
getUid
(getUid this)
Returns a unique identifier for this object
rplaca
(rplaca this value)
replace the first element of this sequence with this value
rplacd
(rplacd this value)
replace the rest (but-first; cdr) of this sequence with this value
pretty-print
(pretty-print cell)
(pretty-print cell width level)
This isn’t the world’s best pretty printer but it sort of works.
Generated by Codox
Beowulf 0.3.0
beowulf.cons-cell
The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, and must have both CAR and CDR mutable, so cannot be implemented on top of Clojure lists.
F
The canonical false value - different from NIL
, which is not canonically false in Lisp 1.5.
make-beowulf-list
(make-beowulf-list x)
Construct a linked list of cons cells with the same content as the sequence x
.
make-cons-cell
(make-cons-cell car cdr)
Construct a new instance of cons cell with this car
and cdr
.
MutableSequence
protocol
Like a sequence, but mutable.
members
getCar
(getCar this)
Return the first element of this sequence.
getCdr
(getCdr this)
like more
, q.v., but returns List NIL
not Clojure nil
when empty.
getUid
(getUid this)
Returns a unique identifier for this object
rplaca
(rplaca this value)
replace the first element of this sequence with this value
rplacd
(rplacd this value)
replace the rest (but-first; cdr) of this sequence with this value
pretty-print
(pretty-print cell)
(pretty-print cell width level)
This isn’t the world’s best pretty printer but it sort of works.
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.core
Essentially, the -main
function and the bootstrap read-eval-print loop.
-main
(-main & opts)
Parse options, print the banner, read the init file if any, and enter the read/eval/print loop.
stop-word
The word which, if submitted an an input line, will cause Beowulf to quit. Question: should this be forlǣte
?
Generated by Codox
Beowulf 0.3.0
beowulf.core
Essentially, the -main
function and the bootstrap read-eval-print loop.
-main
(-main & opts)
Parse options, print the banner, read the init file if any, and enter the read/eval/print loop.
stop-word
The word which, if submitted an an input line, will cause Beowulf to quit. Question: should this be forlǣte
?
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.gendoc
Generate table of documentation of Lisp symbols and functions.
+Generated by Codox
Beowulf 0.3.0
beowulf.gendoc
Generate table of documentation of Lisp symbols and functions.
NOTE: this is very hacky. You almost certainly do not want to use this!
find-documentation
(find-documentation entry)
Find appropriate documentation for this entry
from the oblist.
host-functions
Functions which we can infer are written in Clojure. We need to collect these at run-time, not compile time, hence memoised function, not variable.
infer-signature
(infer-signature entry)
Infer the signature of the function value of this oblist entry
, if any.
infer-type
(infer-type entry)
Try to work out what this entry
from the oblist actually represents.
open-doc
(open-doc symbol)
Open the documentation page for this symbol
, if known, in the default web browser.
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.host
provides Lisp 1.5 functions which can’t be (or can’t efficiently be) implemented in Lisp 1.5, which therefore need to be implemented in the host language, in this case Clojure.
AND
(AND & args)
T
if and only if none of my args
evaluate to either F
or NIL
, else F
.
Generated by Codox
Beowulf 0.3.0
beowulf.host
provides Lisp 1.5 functions which can’t be (or can’t efficiently be) implemented in Lisp 1.5, which therefore need to be implemented in the host language, in this case Clojure.
AND
(AND & args)
T
if and only if none of my args
evaluate to either F
or NIL
, else F
.
In beowulf.host
principally because I don’t yet feel confident to define varargs functions in Lisp.
ASSOC
(ASSOC x a)
If a is an association list such as the one formed by PAIRLIS in the above example, then assoc will produce the first pair whose first term is x. Thus it is a table searching function.
All args are assumed to be beowulf.cons-cell/ConsCell
objects. See page 12 of the Lisp 1.5 Programmers Manual.
NOTE THAT this function is overridden by an implementation in Lisp, but is currently still present for bootstrapping.
ATOM
(ATOM x)
Returns T
if and only if the argument x
is bound to an atom; else F
. It is not clear to me from the documentation whether (ATOM 7)
should return T
or F
. I’m going to assume T
.
ATOM?
macro
(ATOM? x)
The convention of returning F
from predicates, rather than NIL
, is going to tie me in knots. This is a variant of ATOM
which returns NIL
on failure.
CAR
(CAR x)
Return the item indicated by the first pointer of a pair. NIL is treated specially: the CAR of NIL is NIL.
CDR
(CDR x)
Return the item indicated by the second pointer of a pair. NIL is treated specially: the CDR of NIL is NIL.
CONSP
(CONSP o)
Return T
if object o
is a cons cell, else F
.
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.interop
TODO: write docs
INTEROP
(INTEROP fn-symbol args)
Clojure (or other host environment) interoperation API. fn-symbol
is expected to be either
Generated by Codox
Beowulf 0.3.0
beowulf.interop
TODO: write docs
INTEROP
(INTEROP fn-symbol args)
Clojure (or other host environment) interoperation API. fn-symbol
is expected to be either
- a symbol bound in the host environment to a function; or
- a sequence (list) of symbols forming a qualified path name bound to a function. diff --git a/docs/codox/beowulf.io.html b/docs/codox/beowulf.io.html index 687c3b5..d5bae54 100644 --- a/docs/codox/beowulf.io.html +++ b/docs/codox/beowulf.io.html @@ -1,6 +1,6 @@ -
- It reads the meta-expression language
MEXPR
in addition to the symbolic expression languageSEXPR
, which I do not believe the Lisp 1.5 reader ever did;
diff --git a/docs/codox/beowulf.reader.char-reader.html b/docs/codox/beowulf.reader.char-reader.html
index f337b07..a014787 100644
--- a/docs/codox/beowulf.reader.char-reader.html
+++ b/docs/codox/beowulf.reader.char-reader.html
@@ -1,6 +1,6 @@
- - CODING for the MIT-IBM 704 COMPUTER, October 1957 This paper is not about Lisp. But it is about the particular individual computer on which Lisp was first implemented, and it is written in part by members of the Lisp team. I have found it useful in understanding the software environment in which, and the constraints under which, Lisp was written.
- MIT AI Memo 1, John McCarthy, September 1958 This is, as far as I can find, the earliest specification document of the Lisp project. diff --git a/docs/codox/index.html b/docs/codox/index.html index 80e307d..5d09c60 100644 --- a/docs/codox/index.html +++ b/docs/codox/index.html @@ -1,3 +1,3 @@ -
- ADD1
- AND
- ASSOC
- ATOM
- ATOM?
- CAAAAR
- CAAADR
- CAAAR
- CAADAR
- CAADDR
- CAADR
- CAAR
- CADAAR
- CADADR
- CADAR
- CADDAR
- CADDDR
- CADDR
- CADR
- CAR
- CDAAAR
- CDAADR
- CDAAR
- CDADAR
- CDADDR
- CDADR
- CDAR
- CDDAAR
- CDDADR
- CDDAR
- CDDDAR
- CDDDDR
- CDDDR
- CDDR
- CDR
- CONS
- CONSP
- DEFINE
- DEFLIST
- DIFFERENCE
- DOC
- EQ
- EQUAL
- ERROR
- FIXP
- GENSYM
- GET
- GREATERP
- lax?
- LESSP
- LIST
- magic-marker
- NILP
- NULL
- NUMBERP
- OBLIST
- OR
- PAIRLIS
- PLUS
- PUT
- QUOTIENT
- REMAINDER
- RPLACA
- RPLACD
- SET
- SUB1
- TIMES
- TRACE
- traced-symbols
- traced?
- uaf
- UNTRACE
- ADD1
- AND
- ASSOC
- ATOM
- ATOM?
- CAAAAR
- CAAADR
- CAAAR
- CAADAR
- CAADDR
- CAADR
- CAAR
- CADAAR
- CADADR
- CADAR
- CADDAR
- CADDDR
- CADDR
- CADR
- CAR
- CDAAAR
- CDAADR
- CDAAR
- CDADAR
- CDADDR
- CDADR
- CDAR
- CDDAAR
- CDDADR
- CDDAR
- CDDDAR
- CDDDDR
- CDDDR
- CDDR
- CDR
- CONS
- CONSP
- DEFINE
- DEFLIST
- DIFFERENCE
- DOC
- EQ
- EQUAL
- ERROR
- FIXP
- GENSYM
- GET
- GREATERP
- lax?
- LESSP
- LIST
- magic-marker
- NILP
- NULL
- NUMBERP
- OBLIST
- OR
- PAIRLIS
- PLUS
- PUT
- QUOTIENT
- REMAINDER
- RPLACA
- RPLACD
- SET
- SUB1
- TIMES
- TRACE
- traced-symbols
- traced?
- uaf
- UNTRACE
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.io
Non-standard extensions to Lisp 1.5 to read and write to the filesystem.
+Generated by Codox
Beowulf 0.3.0
beowulf.io
Non-standard extensions to Lisp 1.5 to read and write to the filesystem.
Lisp 1.5 had only READ
, which read one S-Expression at a time, and various forms of PRIN*
functions, which printed to the line printer. There was also PUNCH
, which wrote to a card punch. It does not seem that there was any concept of an interactive terminal.
See Appendix E, OVERLORD - THE MONITOR
, and Appendix F, LISP INPUT
AND OUTPUT
.
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.manual
Experimental code for accessing the manual online.
format-page-references
(format-page-references fn-symbol)
Format page references from the manual index for the function whose name is fn-symbol
.
index
This is data extracted from the index pages of Lisp 1.5 Programmer's Manual
. It’s here in the hope that we can automatically link to an online PDF link to the manual when the user invokes a function probably called DOC
or HELP
.
Generated by Codox
Beowulf 0.3.0
beowulf.manual
Experimental code for accessing the manual online.
format-page-references
(format-page-references fn-symbol)
Format page references from the manual index for the function whose name is fn-symbol
.
index
This is data extracted from the index pages of Lisp 1.5 Programmer's Manual
. It’s here in the hope that we can automatically link to an online PDF link to the manual when the user invokes a function probably called DOC
or HELP
.
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.oblist
A namespace mainly devoted to the object list and other top level global variables.
+Generated by Codox
Beowulf 0.3.0
beowulf.oblist
A namespace mainly devoted to the object list and other top level global variables.
Yes, this makes little sense, but if you put them anywhere else you end up in cyclic dependency hell.
NIL
The canonical empty list symbol.
TODO: this doesn’t really work, because (from Clojure) (empty? NIL)
throws an exception. It might be better to subclass beowulf.cons_cell.ConsCell to create a new singleton class Nil which overrides the empty
method of IPersistentCollection?
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.read
This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.
+Generated by Codox
Beowulf 0.3.0
beowulf.read
This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.
Intended deviations from the behaviour of the real Lisp reader are as follows:
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.reader.char-reader
Provide sensible line editing, auto completion, and history recall.
+Generated by Codox
Beowulf 0.3.0
beowulf.reader.char-reader
Provide sensible line editing, auto completion, and history recall.
None of what’s needed here is really working yet, and a pull request with a working implementation would be greatly welcomed.
What’s needed (rough specification)
-
diff --git a/docs/codox/beowulf.reader.generate.html b/docs/codox/beowulf.reader.generate.html
index 36c5dd7..58d4e15 100644
--- a/docs/codox/beowulf.reader.generate.html
+++ b/docs/codox/beowulf.reader.generate.html
@@ -1,6 +1,6 @@
-
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.reader.generate
Generating S-Expressions from parse trees.
+Generated by Codox
Beowulf 0.3.0
beowulf.reader.generate
Generating S-Expressions from parse trees.
From Lisp 1.5 Programmers Manual, page 10
Note that I’ve retyped much of this, since copy/pasting out of PDF is less than reliable. Any typos are mine.
Quote starts:
diff --git a/docs/codox/beowulf.reader.macros.html b/docs/codox/beowulf.reader.macros.html index b2fa009..19c4982 100644 --- a/docs/codox/beowulf.reader.macros.html +++ b/docs/codox/beowulf.reader.macros.html @@ -1,5 +1,5 @@ -Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.reader.macros
Can I implement reader macros? let’s see!
+Generated by Codox
Beowulf 0.3.0
beowulf.reader.macros
Can I implement reader macros? let’s see!
We don’t need (at least, in the Clojure reader) to rewrite forms like 'FOO
, because that’s handled by the parser. But we do need to rewrite things which don’t evaluate their arguments, like SETQ
, because (unless LABEL does it, which I’m not yet sure of) we’re not yet able to implement things which don’t evaluate arguments.
TODO: at this stage, the following should probably also be read macros: DEFINE
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.reader.parser
The actual parser, supporting both S-expression and M-expression syntax.
parse
Parse a string presented as argument into a parse tree which can then be operated upon further.
Generated by Codox
Beowulf 0.3.0
beowulf.reader.parser
The actual parser, supporting both S-expression and M-expression syntax.
parse
Parse a string presented as argument into a parse tree which can then be operated upon further.
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf.reader.simplify
Simplify parse trees. Be aware that this is very tightly coupled with the parser.
simplify
(simplify p)
Simplify this parse tree p
. If p
is an instaparse failure object, throw an ex-info
, with p
as the value of its :failure
key. Calls remove-optional-space
before processing.
simplify-tree
(simplify-tree p)
(simplify-tree p context)
Simplify this parse tree p
. If p
is an instaparse failure object, throw an ex-info
, with p
as the value of its :failure
key.
Generated by Codox
Beowulf 0.3.0
beowulf.reader.simplify
Simplify parse trees. Be aware that this is very tightly coupled with the parser.
simplify
(simplify p)
Simplify this parse tree p
. If p
is an instaparse failure object, throw an ex-info
, with p
as the value of its :failure
key. Calls remove-optional-space
before processing.
simplify-tree
(simplify-tree p)
(simplify-tree p context)
Simplify this parse tree p
. If p
is an instaparse failure object, throw an ex-info
, with p
as the value of its :failure
key.
NOTE THAT it is assumed that remove-optional-space
has been run on the parse tree BEFORE it is passed to simplify-tree
.
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
Further Reading
+Generated by Codox
Beowulf 0.3.0
Further Reading
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
Beowulf 0.3.0-SNAPSHOT
Released under the GPL-2.0-or-later
LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.
Installation
To install, add the following dependency to your project or build file:
[beowulf "0.3.0-SNAPSHOT"]
Topics
Namespaces
beowulf.bootstrap
Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual
; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host
, be sufficient to bootstrap the full Lisp 1.5 interpreter..
Public variables and functions:
beowulf.cons-cell
The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, and must have both CAR and CDR mutable, so cannot be implemented on top of Clojure lists.
Public variables and functions:
beowulf.core
Essentially, the -main
function and the bootstrap read-eval-print loop.
Public variables and functions:
beowulf.gendoc
Generate table of documentation of Lisp symbols and functions.
Public variables and functions:
beowulf.host
provides Lisp 1.5 functions which can’t be (or can’t efficiently be) implemented in Lisp 1.5, which therefore need to be implemented in the host language, in this case Clojure.
Public variables and functions:
beowulf.io
Non-standard extensions to Lisp 1.5 to read and write to the filesystem.
Public variables and functions:
beowulf.oblist
A namespace mainly devoted to the object list and other top level global variables.
beowulf.read
This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.
Public variables and functions:
beowulf.reader.char-reader
Provide sensible line editing, auto completion, and history recall.
Public variables and functions:
beowulf.reader.parser
The actual parser, supporting both S-expression and M-expression syntax.
Public variables and functions:
beowulf.reader.simplify
Simplify parse trees. Be aware that this is very tightly coupled with the parser.
Public variables and functions:
Generated by Codox
Beowulf 0.3.0
Beowulf 0.3.0
Released under the GPL-2.0-or-later
LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.
Installation
To install, add the following dependency to your project or build file:
[beowulf "0.3.0"]
Topics
Namespaces
beowulf.bootstrap
Lisp as defined in Chapter 1 (pages 1-14) of the Lisp 1.5 Programmer's Manual
; that is to say, a very simple Lisp language, which should, I believe, be sufficient in conjunction with the functions provided by beowulf.host
, be sufficient to bootstrap the full Lisp 1.5 interpreter..
Public variables and functions:
beowulf.cons-cell
The fundamental cons cell on which all Lisp structures are built. Lisp 1.5 lists do not necessarily have a sequence as their CDR, and must have both CAR and CDR mutable, so cannot be implemented on top of Clojure lists.
Public variables and functions:
beowulf.core
Essentially, the -main
function and the bootstrap read-eval-print loop.
Public variables and functions:
beowulf.gendoc
Generate table of documentation of Lisp symbols and functions.
Public variables and functions:
beowulf.host
provides Lisp 1.5 functions which can’t be (or can’t efficiently be) implemented in Lisp 1.5, which therefore need to be implemented in the host language, in this case Clojure.
Public variables and functions:
beowulf.io
Non-standard extensions to Lisp 1.5 to read and write to the filesystem.
Public variables and functions:
beowulf.oblist
A namespace mainly devoted to the object list and other top level global variables.
beowulf.read
This provides the reader required for boostrapping. It’s not a bad reader - it provides feedback on errors found in the input - but it isn’t the real Lisp reader.
Public variables and functions:
beowulf.reader.char-reader
Provide sensible line editing, auto completion, and history recall.
Public variables and functions:
beowulf.reader.parser
The actual parser, supporting both S-expression and M-expression syntax.
Public variables and functions:
beowulf.reader.simplify
Simplify parse trees. Be aware that this is very tightly coupled with the parser.
Public variables and functions:
Generated by Codox
Beowulf 0.3.0-SNAPSHOT
beowulf
+Generated by Codox
Beowulf 0.3.0
beowulf
Þý liste cræfte spræc
LISP 1.5 is to all Lisp dialects as Beowulf is to English literature.
You are of course welcome to fork the project and do whatever you like with it!
Invoking
Invoke with
-java -jar target/uberjar/beowulf-0.3.0-SNAPSHOT-standalone.jar --help
+java -jar target/uberjar/beowulf-0.3.0-standalone.jar --help
(Obviously, check your version number)
Command line arguments as follows:
diff --git a/docs/codox/mexpr.html b/docs/codox/mexpr.html
index 19ef964..7692fe9 100644
--- a/docs/codox/mexpr.html
+++ b/docs/codox/mexpr.html
@@ -1,6 +1,6 @@
-Interpreting M-Expressions Generated by Codox
Beowulf 0.3.0-SNAPSHOT
Interpreting M-Expressions
+Interpreting M-Expressions Generated by Codox
Beowulf 0.3.0
Interpreting M-Expressions
M-Expressions (‘mexprs’) are the grammar which John McCarthy origininally used to write Lisp, and the grammar in which many of the function definitions in the Lisp 1.5 Programmer’s Manual are stated. However, I have not seen anywhere a claim that Lisp 1.5 could read M-Expressions, and it is not clear to me whether it was even planned that it should do so, although the discussion on page 10 suggests that it was.
Rather, it seems to me possible that M-Expressions were only ever a grammar intended to be written on paper, like Backus Naur Form, to describe and to reason about algorithms. I think at the point at which the M-Expression grammar was written, the idea of the universal Lisp function
I set out to make Beowulf read M-Expressions essentially out of curiousity, to see whether it could be done. I had this idea that if it could be done, I could implement most of Lisp 1.5 simply by copying in the M-Expression definitions out of the manual.
diff --git a/docs/codox/values.html b/docs/codox/values.html
index 6337cb1..1bfdc12 100644
--- a/docs/codox/values.html
+++ b/docs/codox/values.html
@@ -1,6 +1,6 @@
-The properties of the system, and their values Generated by Codox
Beowulf 0.3.0-SNAPSHOT
The properties of the system, and their values
+The properties of the system, and their values Generated by Codox
Beowulf 0.3.0
The properties of the system, and their values
here be dragons
Lisp is the list processing language; that is what its name means. It processes data structures built of lists - which may be lists of lists, or lists of numbers, or lists of any other sort of data item provided for by the designers of the system.
But how is a list, in a computer, actually implemented?
diff --git a/project.clj b/project.clj
index 976a128..b57eeb4 100644
--- a/project.clj
+++ b/project.clj
@@ -1,4 +1,4 @@
-(defproject beowulf "0.3.0-SNAPSHOT"
+(defproject beowulf "0.3.0"
:aot :all
:cloverage {:output "docs/cloverage"
:ns-exclude-regex [#"beowulf\.gendoc" #"beowulf\.scratch"]}