diff --git a/README.md b/README.md
index fe22a99..04b7c9a 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ such as modules in an ESI file.
## Language Specification
-The XNeedle language consists of three distinct parts.
+The XN language consists of three distinct parts.
1. Expressions
@@ -59,16 +59,16 @@ This example adds, removes and swaps nodes.
$("RxPdo/Index[.="#x1600"]/..")
{
# Modify name
- $(Name) { text("FSoE Outputs"); }
+ $("Name") { text("FSoE Outputs"); }
# Select the Entry/Index #x7000 and change it to #x9001
- $(Entry/Index[.="#x7000"]) { text("#x9001"); }
+ $("Entry/Index[.='#x7000']") { text("#x9001"); }
# Select the Entry with Index #x7001 and SubIndex 4, remove it
- $(Entry/Index[.="#x7001"]/../SubIndex[.="4"]/..) { remove(); }
+ $("Entry/Index[.='#x7001']/../SubIndex[.="4"]/..") { remove(); }
# Select the 3rd Entry
- $(Entry[3])
+ $("Entry[3]")
{
# Add a tag before the above selection
!before("Entry")
diff --git a/doc/01_syntax.md b/doc/01_syntax.md
new file mode 100644
index 0000000..b217e87
--- /dev/null
+++ b/doc/01_syntax.md
@@ -0,0 +1,76 @@
+# XN Syntax
+
+## Comments
+
+Comments are single line only prefixed with the pound sign ('#').
+
+## Rules
+
+Rules consists of two parts, a selector, and a block containing statements.
+A selector is an XPath string surrounded with braces prefixed with a dollar sign.
+The XPath selects zero or more document nodes which will serve as the context for the statement block.
+
+## Bangs
+
+Bangs are function calls combined with a statement block.
+They are prefixed with an exclaimation mark.
+A bang function always produces context nodes for the statement block.
+
+## Statement Block
+A statement block are multiple statements terminated with a semicolon,
+surrounded by curly braces.
+
+## Statements
+
+A statement can be an expression, a function call, a rule or a bang.
+To note is that a nested rule will use its parents context to refine the selection further.
+
+## Expressions
+
+An expression can consist of function calls, operators, strings or numbers.
+
+Operators supported:
+
+| Operator | Function |
+|----------|----------------------|
+| + | addition |
+| - | subtraction |
+| * | multiplication |
+| / | division |
+| ** | power |
+| & | bitwise and |
+| | | bitwise or |
+| ^ | bitwise exclusive or |
+| .. | string concatenate |
+
+## Example
+
+```
+# Select the RxPdo with Index 0x1600
+$("RxPdo/Index[.="#x1600"]/..")
+{
+ # Function call
+ swap("Entry[3]", "Entry[4]");
+
+ # Of that RxPdo, select the 3rd Entry via a nested rule
+ $("Entry[3]")
+ {
+ # This bang has "RxPdo/Index[.="#x1600"]/../Entry[3]" as its context
+ !before("Entry")
+ {
+ # Function call contains an expression, which evaluates here to "!#xFFFF"
+ !add("Index") { text("!"..hexlit(2**16-1)); };
+ !add("SubIndex") { text("3"); }
+ !add("BitLen") { text("256"); }
+ !add("Name") { text("Test"); }
+ !add("DataType") { text("UINT"); }
+ }
+ }
+}
+
+# One .xn can contain multiple rules
+$("//Module[@Crc32]")
+{
+ attr("Crc32", hexlit(crc32()));
+};
+```
\ No newline at end of file
diff --git a/doc/02_functions.md b/doc/02_functions.md
new file mode 100644
index 0000000..3ce9cf6
--- /dev/null
+++ b/doc/02_functions.md
@@ -0,0 +1,204 @@
+# Function Reference
+
+## `num(value:string, numbase:number = 10):number`
+
+Converts a string to a number in respect of the input base.
+
+Example:
+```xn
+# Add 1 to all LcId
+$("//Name[@LcId]")
+{
+ attr("LcId", str(num(attr("LcId")) + 1));
+};
+```
+
+## `str(value:number, numbase:number = 10):string`
+
+Converts a number to a string in any base.
+
+Example:
+```xn
+# Set ProfileNo to "#o377"
+$("//ProfileNo")
+{
+ text("#o"..str(2**8-1, 8));
+};
+
+```
+
+## `hexlit(value:number):string`
+
+Produces a hex literal from an integer with lowercase digits.
+
+Example:
+```xn
+# Set all module Crc32 to #x00c0ffee
+$("//Module[@Crc32]")
+{
+ attr("Crc32", hexlit(0xC0FFEE));
+};
+```
+
+
+## `parselit(literal:string, base:number = 16):string`
+
+Parses a literal number to an integer.
+
+Example:
+```xn
+# Increase all RevisionNo by 1
+$("//Device/Type[@RevisionNo]")
+{
+ attr("RevisionNo", hexlit(parselit(attr("RevisionNo")) + 1));
+};
+```
+
+## `lower(text:string):string`
+
+Converts a string to all lower case text.
+
+Example:
+```xn
+# Convert all device names to lower case
+$("//Device/Type")
+{
+ $("Name") { cdata(lower(cdata())); };
+}
+```
+
+## `upper(text:string):string`
+
+Converts a string to all upper case text.
+
+Example:
+```xn
+# Convert all Name tag text in Index nodes from RxPdo to upper case.
+$("//RxPdo/Index/Name")
+{
+ $("Name") { text(upper(text())); };
+}
+```
+
+## `crc32(poly:number = 0x04C11DB7, init:number = 0, xorOut:number = 0, reflected:bool = false):number`
+
+Calculates the CRC32 checksum from the rendered document starting after the tag close of the context
+node up to and including the closing tag.
+
+Example:
+
+For this example `[IND]` is whitespace for indent,
+`[CR]` for carriage return and `[LF]` for line feed.
+
+Rendered document:
+```xml
+[CR][LF]
+[IND][CR][LF]
+[IND][IND]
+[IND][IND]Text
+[IND]
+
+
+XN
+```xn
+# Calculate CRC of node and store in "crc" attribute as uppercase hexlit
+$("//Node") { attr("crc", upper(hexlit(crc32())); }
+```
+
+Digested fragment:
+```txt
+[CR][LF]
+[IND][IND]
+[IND][IND]Text
+[IND]
+```
+
+## `text():string` / `text(value:string):string`
+
+Get or set the text of the context node.
+
+Example:
+```xn
+# Prefix all Name tag text in Index nodes from TxPdo with "Fault".
+$("//TxPdo/Index/Name")
+{
+ $("Name") { text("Fault" .. text()); };
+}
+```
+
+## `cdata():string` / `cdata(value:string):string`
+
+Get or set the CDATA of the context node.
+
+Example:
+```xn
+# For Device with this ProductCode and Revision, modify its CDATA text
+$("//Device/Type[@ProductCode='#x0B583052' and @RevisionNo='#x00110000']/..")
+{
+ $("Name[@LcId='1033']") { cdata("LE2904, 4 Ch. Danger Output 24V, 0.5A, TwinUNSAFE"); };
+}
+```
+
+## `attr(name:string):string` / `attr(name:string, value:string):string`
+
+Get or set an attribute of the context node by name.
+
+Example:
+```xn
+# For Device with this ProductCode and Revision, set the revision to #xc0deaffe
+$("//Device/Type[@ProductCode='#x0B583052' and @RevisionNo='#x00110000']")
+{
+ attr("RevisionNo", hexlit(0xC0DEAFFE));
+}
+```
+
+## `swap(xpath_a:string, xpath_b:string):string = ""`
+
+Swaps two nodes by XPath in the current cotext node.
+
+Example:
+```xn
+# Swap first and second Entry in RxPdo of Index 0x1600
+$("//RxPdo/Index[.='#x1600']/..") { swap("Entry[1]", "Entry[2]"); };
+
+```
+
+## `remove():string = ""`
+
+Removes the context node from the document.
+
+Example:
+```xn
+# Remove all modules
+$("//Module") { remove(); };
+```
+
+## `rename(name:string):string = ""`
+
+Renames the context node.
+
+Example:
+```xn
+# Rename all Device nodes to DeviceX
+$("//Device") { rename("DeviceX"); };
+```
+
+## `rename(xpath:string, name:string):string = ""`
+
+Renames any nodes matching the XPath in the context node.
+
+Example:
+```xn
+# Rename all Device nodes Name tags to Label
+$("//Device") { rename("Name", "Label"); };
+```
+
+## `require(message:string):string = ""`
+
+If the context node is not present, emit an error message.
+
+Example:
+```xn
+# Emit an error if no Module tags are in the document
+$("//Module") { require("No Module definitions!"); };
+```
diff --git a/doc/03_bangs.md b/doc/03_bangs.md
new file mode 100644
index 0000000..def1648
--- /dev/null
+++ b/doc/03_bangs.md
@@ -0,0 +1,53 @@
+# Bang Reference
+
+## `!add(name:string)`
+
+Appends a new node into the context.
+
+Example:
+```xn
+# Add 1 to all LcId
+$("//RxPdo/Index[.="#x1600"]/..")
+{
+ # Add a new Entry with attribute "added" set to "true"
+ !add("Entry")
+ {
+ attr("added", "true");
+ }
+};
+```
+
+## `!before(name:string)`
+
+Appends a new node before the context.
+
+Example:
+```xn
+# Add 1 to all LcId
+$("//RxPdo/Index[.="#x1600"]/..")
+{
+ # Add a new TxPdo with attribute "added" set to "true" before the above node selection
+ !before("TxPdo")
+ {
+ attr("added", "true");
+ }
+};
+```
+
+## `!after(name:string)`
+
+Appends a new node after the context.
+
+Example:
+```xn
+# Add 1 to all LcId
+$("//RxPdo/Index[.="#x1600"]/..")
+{
+ # Add a new TxPdo with attribute "added" set to "true" after the above node selection
+ !after("TxPdo")
+ {
+ attr("added", "true");
+ }
+};
+```
+