From 0be032c6998e712dc2c9f2ed97c3491d89eb05af Mon Sep 17 00:00:00 2001 From: Aleksey Veresov Date: Fri, 31 Jan 2020 17:16:27 +0300 Subject: [xift] Almost done. --- src/xift.cpp | 358 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 src/xift.cpp (limited to 'src/xift.cpp') diff --git a/src/xift.cpp b/src/xift.cpp new file mode 100644 index 0000000..8b703cb --- /dev/null +++ b/src/xift.cpp @@ -0,0 +1,358 @@ +#include "xift.hpp" + +#include "utils.hpp" +#include + + +const char *XiftErrorMessage(XiftError error) +{ + switch (error) { + case xift_ok: return ""; + case xift_wrong_char: return "Wrong character in tag."; + case xift_attr_sep: return "Attribute separator missed."; + case xift_something_after_close: return "Met something after tag close."; + case xift_close_selfclose: return "Tag is closing and self-closing."; + case xift_unwanted_close: return "Closing not opened tag."; + } +} + + +Xift::Xift(XiftExporter &exporter): + error_pos(0), error_line(1), error_column(0), exporter(exporter), + state(reading_text), is_closing(false), is_self_closing(false) +{} + + +void Xift::Put(char c) +{ + if (state != state_error) { + ++error_pos; + if (c == '\n') { + ++error_line; + error_column = 0; + } + ++error_column; + } + switch (state) { + case state_error: break; + case reading_text: ReadText(c); break; + case opening_tag: OpenTag(c); break; + case reading_name: ReadName(c); break; + case waiting_attr: WaitAttr(c); break; + case reading_attr_name: ReadAttrName(c); break; + case waiting_attr_sep: WaitAttrSep(c); break; + case waiting_attr_value: WaitAttrValue(c); break; + case reading_attr_value: ReadAttrValue(c); break; + case closing_tag: CloseTag(c); break; + } +} + +void Xift::Put(const char *str) +{ + while (*str) { + Put(*str); + ++str; + } +} + +void Xift::Put(FILE *file) +{ + if (file) { + for (int c = fgetc(file); c != EOF; c = fgetc(file)) { + Put(c); + } + } +} + +void Xift::PutFile(const char *path) +{ + FILE *file = fopen(path, "r"); + Put(file); + fclose(file); +} + + +bool Xift::End() +{ + if (state != state_error) { + while (opened.Top()) { + CloseTop(); + } + } + return !IsError(); +} + + +bool Xift::IsError() { return state == state_error;} +int Xift::ErrorPosition() { return error_pos; } +int Xift::ErrorLine() { return error_line; } +int Xift::ErrorColumn() { return error_column; } +XiftError Xift::GetError() { return error; } + + +void Xift::ReadText(char c) +{ + switch (c) { + case '<': state = opening_tag; break; + case '>': exporter.Put(">"); break; + default: exporter.Put(c); break; + } +} + +void Xift::OpenTag(char c) +{ + Item &t = opened.New(); + if (c == '/') { + is_closing = true; + state = reading_name; + } else if (AllowedInTokenStart(c)) { + xift_str_add(t.name, t.len, t.size, c); + state = reading_name; + } else { + state = state_error; + error = xift_wrong_char; + } +} + +void Xift::ReadName(char c) +{ + if (AllowedInToken(c)) { + Item &t = *opened.Top(); + xift_str_add(t.name, t.len, t.size, c); + } else if (IsWhitespace(c)) { + state = waiting_attr; + } else if (c == '>') { + CompleteCurrent(); + state = reading_text; + } else { + state = state_error; + error = xift_wrong_char; + } +} + +void Xift::WaitAttr(char c) +{ + if (AllowedInTokenStart(c)) { + Item::Attr &a = opened.Top()->New(); + xift_str_add(a.name, a.nlen, a.nsize, c); + state = reading_attr_name; + } else if (c == '/') { + is_self_closing = true; + state = closing_tag; + } else if (c == '>') { + CompleteCurrent(); + state = reading_text; + } else if (!IsWhitespace(c)) { + state = state_error; + error = xift_wrong_char; + } +} + +void Xift::ReadAttrName(char c) +{ + if (AllowedInToken(c)) { + Item::Attr &a = *opened.Top()->Top(); + xift_str_add(a.name, a.nlen, a.nsize, c); + } else if (c == '=') { + state = waiting_attr_value; + } else if (c == '>') { + Item::Attr &a = *opened.Top()->Top(); + a.value = xift_str_create_copy(a.name, a.name + a.len); + a.vlen = a.nlen; + a.vsize = a.vlen + 1; + a.value_quota = '\''; + CompleteCurrent(); + state = reading_text; + } else if (IsWhitespace(c)) { + state = waiting_attr_sep; + } else { + state = state_error; + error = xift_wrong_char; + } +} + +void Xift::WaitAttrSep(char c) +{ + if (c == '=') { + state = waiting_attr_value; + } else if (c == '>') { + Item::Attr &a = *opened.Top()->Top(); + a.value = xift_str_create_copy(a.name, a.name + a.len); + a.vlen = a.nlen; + a.vsize = a.vlen + 1; + a.value_quota = '\''; + CompleteCurrent(); + state = reading_text; + } else if (AllowedInTokenStart(c)) { + Item::Attr &a = *opened.Top()->Top(); + a.value = xift_str_create_copy(a.name, a.name + a.len); + a.vlen = a.nlen; + a.vsize = a.vlen + 1; + a.value_quota = '\''; + Item::Attr &n = opened.Top()->New(); + xift_str_add(n.name, n.nlen, n.nsize, c); + state = reading_attr_name; + } else if (!IsWhitespace(c)) { + state = state_error; + error = xift_attr_sep; + } +} + +void Xift::WaitAttrValue(char c) +{ + if (c == '"' || c == '\'') { + opened.Top()->Top()->value_quota = c; + value_quota = c; + state = reading_attr_value; + } else if (AllowedInTokenStart(c)) { + Item::Attr &a = *opened.Top()->Top(); + a.value_quota = '\''; + xift_str_add(a.value, a.vlen, a.vsize, c); + value_quota = 0; + state = reading_attr_value; + } else { + state = state_error; + error = xift_wrong_char; + } +} + +void Xift::ReadAttrValue(char c) +{ + if (value_quota) { + if (c == value_quota) { + state = waiting_attr; + } else { + Item::Attr &a = *opened.Top()->Top(); + xift_str_add(a.value, a.vlen, a.vsize, c); + } + } else { + if (c == '>') { + CompleteCurrent(); + state = reading_text; + } + if (AllowedInToken(c)) { + Item::Attr &a = *opened.Top()->Top(); + xift_str_add(a.value, a.vlen, a.vsize, c); + } else if (IsWhitespace(c)) { + state = waiting_attr; + } else { + state = state_error; + error = xift_wrong_char; + } + } +} + +void Xift::CloseTag(char c) +{ + if (c == '>') { + CompleteCurrent(); + state = reading_text; + } else if (!IsWhitespace(c)) { + state = state_error; + error = something_after_close; + } +} + + +void Xift::CompleteCurrent() +{ + if (ContainsMatchedForm(*opened.Top())) { + if (is_closing && is_self_closing) { + state = state_error; + error = xift_close_selfclose; + } else if (is_closing) { + CloseCurrent(); + } else if (is_self_closing) { + SelfcloseCurrent(); + } else { + PutCurrent(); + } + } else { + opened.Pop(); + } + is_self_closing = false; + is_closing = false; +} + +void Xift::CloseCurrent() +{ + XiftTag *closing = opened.PopToBeDeleted(); + if (closing) { + while (opened.Top() && !opened.Top()->MatchesForm(*closing)) { + CloseTop(); + } + delete closing; + if (opened.Top()) { + CloseTop(); + } else { + state = state_error; + error = xift_unwanted_close; + } + } +} + +void Xift::CloseTop() +{ + CloseName(opened.Top()->name); + opened.Pop(); +} + +void Xift::CloseName(const ScriptVariable &name) +{ + exporter.Put("'); +} + +void Xift::SelfcloseCurrent() +{ + Item &t = *opened.Top(); + exporter.Put('<'); + exporter.Put(t.name); + Item::Attr *a; + while (a = t.Top()) { + exporter.Put(' '); + exporter.Put(a->name); + exporter.Put('='); + exporter.Put(a->value_quota); + exporter.Put(a->value); + exporter.Put(a->value_quota); + t.Pop(); + } + exporter.Put("/>"); + opened.Pop(); +} + +void Xift::PutCurrent() +{ + Item &t = *opened.Top(); + exporter.Put('<'); + exporter.Put(t.name); + Item::Attr *a; + while (a = t.Top()) { + exporter.Put(' '); + exporter.Put(a->name); + exporter.Put('='); + exporter.Put(a->value_quota); + exporter.Put(a->value); + exporter.Put(a->value_quota); + t.Pop(); + } + exporter.Put('>'); +} + + +bool Xift::AllowedInTokenStart(char c) +{ + return c == ':' || c == '_' || isalpha(c); +} + +bool Xift::AllowedInToken(char c) +{ + return c == '-' || c == '.' || isdigit(c) || AllowedInTokenStart(c); +} + +bool Xift::IsWhitespace(char c) +{ + return c == ' ' || c == '\t' || c == '\n'; +} -- cgit v1.2.3