From 4ffe1f0ad93f28aba9b9f858d95fd194aec9d0bd Mon Sep 17 00:00:00 2001 From: DarkWiiPlayer Date: Fri, 20 Dec 2024 23:21:46 +0100 Subject: [PATCH] Finish implementing OATS --- readme.md | 35 ++++++++++++++++++++++++++++--- spec/fixtures/files/document.oats | 13 +++--------- spec/fixtures/files/multi.oats | 4 ++++ spec/oats_spec.moon | 29 +++++++++++++++---------- src/oats.lua | 34 ++++++++++++++++++++++++++++-- 5 files changed, 89 insertions(+), 26 deletions(-) create mode 100644 spec/fixtures/files/multi.oats diff --git a/readme.md b/readme.md index f75dd9a..fd2f3f4 100644 --- a/readme.md +++ b/readme.md @@ -25,15 +25,40 @@ Nested elements are indented nested text ``` -Multi-line text nodes are yet to be decided. As of now, the options are: +Tags with only one text node can be shortened to one-line tags: -1. Consecutive non-empty lines of text are merged with a space -2. Any non-empty text line is always a single text element +``` +[tag-name] single text node + +evaluates to the same as + +[tag-name] + single text node +``` + +Text nodes mixed with one-line tags can further be shortened with inline tags: + +``` +Plain text with some +[bold] tagged +text +[emphasis] in between + +evaluates to the same as + +Plain text with some [bold tagged] text [emphasis in between] +``` **Note**: Consumers may have a better understanding of whether and how to join text elements together, while the interpreter would have to decide on a joining strategy (most likely concatenation with a space character in between). +### Data Type + +OATS makes no attempts to interpret text. +Everything is considered a string and it is left up to the consuming application +to decide how to interpret the textual representation. + ### Conventions OATS is a very simple format without many restrictions. @@ -45,4 +70,8 @@ generally ignore case. Tag names should use lowercase kebab-case. +Applications that interpret parts of a tag name as a namespace should use a +single colon `:` as the namespace separator, with namespaces preceding the tag +name. + ## Interface diff --git a/spec/fixtures/files/document.oats b/spec/fixtures/files/document.oats index 0d591de..7bc5d9d 100644 --- a/spec/fixtures/files/document.oats +++ b/spec/fixtures/files/document.oats @@ -1,12 +1,5 @@ [document] [section] - [title] Document - - Paragraph - - Multiline - Paragraph - - Text with a [nested nested] node - - Text with an [nested] empty nested node + Text with a [inline inline] node + Text with an [inline] empty nested node + [one-line] with [inline] node diff --git a/spec/fixtures/files/multi.oats b/spec/fixtures/files/multi.oats new file mode 100644 index 0000000..662b8ca --- /dev/null +++ b/spec/fixtures/files/multi.oats @@ -0,0 +1,4 @@ +multi +line + +strings diff --git a/spec/oats_spec.moon b/spec/oats_spec.moon index 2db041c..47698fa 100644 --- a/spec/oats_spec.moon +++ b/spec/oats_spec.moon @@ -20,25 +20,32 @@ describe "OATS", -> it "parses basic text nodes", -> bob = {name: "person", {name: "name", "Bob"}, {name: "age", "20"}} assert.same {bob}, oats.decodefile("spec/fixtures/files/bob.oats") + + it "parses multiple lines into separate nodes", -> + assert.same {"multi", "line", "strings"}, oats.decodefile("spec/fixtures/files/multi.oats") it "parses one-line nodes", -> rose = {name: "person", {name: "name", "Rose"}, {name: "age", "22"}} assert.same {rose}, oats.decodefile("spec/fixtures/files/rose.oats") - pending "parses inline nodes", -> + it "parses inline nodes", -> document = { name: "document" - {name: "title", "Document"} - "Paragraph" - "Multiline Paragraph" - "Text with a" - {name: "nested", "nested"} - "node" - "Text with an" - {name: "nested"} - "empty nested node" + { + name: "section" + "Text with a " + {name: "inline", "inline"} + " node" + "Text with an " + {name: "inline"} + " empty nested node" + {name: "one-line", "with ", {name: "inline"}, " node"} + } } - assert.same {document}, oats.decodefile("spec/fixtures/files/document.oats") + assert.same document, oats.decodefile("spec/fixtures/files/document.oats")[1] + + it "errors for endless inline nodes", -> + assert.has.error (-> oats.decode("line with [endless tag")), "Endless inline tag on 1:11" it "parses strings files", -> assert.same {{name: "tester"}}, oats.decode("[tester]") diff --git a/src/oats.lua b/src/oats.lua index c84effc..ffa8410 100644 --- a/src/oats.lua +++ b/src/oats.lua @@ -30,6 +30,36 @@ local function handledepth(callback, last, current, number) end end +local function parsetext(callback, text, number) + local start = 1 + local inline while true do + inline = text:find("[", start, true) + if inline then + callback("text", text:sub(start, inline - 1)) + local close = text:find("]", inline, true) + if close then + local inline_content = text:sub(inline+1, close-1) + local inline_name, inline_text + inline_name, inline_text = inline_content:match("^([^%s]+)%s+(.+)$") + if not inline_name then + inline_name = inline_content + end + callback("open", inline_name) + if inline_text then + callback("text", inline_text) + end + callback("close") + start = close + 1 + else + error(string.format("Endless inline tag on %i:%i", number, inline)) + end + else + break + end + end + callback("text", text:sub(start)) +end + --- @param callback callback --- @param line string --- @param number number Line number currently being processed @@ -47,7 +77,7 @@ local function parseline(callback, line, lastdepth, number) if depth then handledepth(callback, lastdepth, depth, number) callback("open", name) - callback("text", text) + parsetext(callback, text, number) callback("close") return depth-1 -- Minus one because the tag is already closed end @@ -59,7 +89,7 @@ local function parseline(callback, line, lastdepth, number) depth, text = line:match("^\t*()(.*)$") if depth then handledepth(callback, lastdepth, depth, number) - callback("text", text) + parsetext(callback, text, number) return depth-1 -- Minus one because text doesn't get closed end end