diff --git a/main.vala b/main.vala index 105db6e..d07751a 100644 --- a/main.vala +++ b/main.vala @@ -4,30 +4,19 @@ using Pluie; int main (string[] args) { - Echo.init(true); + Echo.init(false); var path = "resources/test.yml"; var done = false; of.title ("Pluie Yaml Parser", Pluie.Yaml.VERSION, "a-sansara"); - of.action ("Reading file", path); - of.echo (); - var reader = new Io.Reader (path); - while (reader.readable) { - of.echo ("%s %s".printf ( - of.c (ECHO.DATE ).s ("%03d |".printf (reader.line)), - of.c (ECHO.OPTION_SEP).s (reader.read ())) - ); + var loader = new Yaml.Loader (path, true, true); + if ((done = loader.done)) { + Yaml.NodeRoot root = loader.get_nodes (); } - of.echo ("EOF"); - of.state (true); - of.action ("Parsing file", path); - var processor = new Yaml.Processor (path); - done = processor.done; - - of.state (done); + of.rs (done); of.echo (); return (int) done; diff --git a/meson.build b/meson.build index aecb988..9f535fe 100644 --- a/meson.build +++ b/meson.build @@ -33,7 +33,10 @@ sources = [ 'src/vala/Pluie/Io.Reader.vala', 'src/vala/Pluie/Io.StreamLineMark.vala', 'src/vala/Pluie/Yaml.global.vala', + 'src/vala/Pluie/Yaml.Document.vala', 'src/vala/Pluie/Yaml.Event.vala', + 'src/vala/Pluie/Yaml.Loader.vala', + 'src/vala/Pluie/Yaml.Scanner.vala', 'src/vala/Pluie/Yaml.Processor.vala', 'src/vala/Pluie/Yaml.BaseNode.vala', 'src/vala/Pluie/Yaml.Node.vala', diff --git a/src/vala/Pluie/Yaml.BaseNode.vala b/src/vala/Pluie/Yaml.BaseNode.vala index ed707ae..587d4bb 100644 --- a/src/vala/Pluie/Yaml.BaseNode.vala +++ b/src/vala/Pluie/Yaml.BaseNode.vala @@ -12,6 +12,8 @@ public class Pluie.Yaml.BaseNode : Object, Pluie.Yaml.Node */ public string uuid { get; internal set; } + public string anchor { get; internal set; } + /** * find mode related to Yaml.FIND_MODE, default is Yaml.FIND_MODE.SQUARE_BRACKETS */ @@ -75,6 +77,23 @@ public class Pluie.Yaml.BaseNode : Object, Pluie.Yaml.Node this.uuid = Yaml.uuid (); } + /** + * + */ + protected virtual void set_anchor_id (string id) + { + this.anchor = id; + } + + /** + * clone current node + * @param the name of clone + */ + public virtual Yaml.Node clone_node (string? name = null) + { + return new BaseNode.standard (this.parent, this.indent); + } + /** * stuff on changing parent node * @param child the childto add @@ -172,17 +191,17 @@ public class Pluie.Yaml.BaseNode : Object, Pluie.Yaml.Node public string to_string (bool withIndent = true) { - return "%s%s%s%s%s%s%s%s".printf ( + return "%s%s%s%s%s%s%s".printf ( this.node_type.is_root () ? "" : of.s_indent ((int8) (withIndent ? this.indent : 0)), of.c (ECHO.OPTION).s ("<"), of.c (ECHO.OPTION_SEP).s (this.node_type.infos ()), this.name != null ? " %s".printf (this.name) - : (this.node_type.is_scallar () ? " %s".printf (this.data) : ""), + : (this.node_type.is_scalar () ? " %s".printf (this.data) : ""), "[%x]".printf (this.ref_count), this.parent == null ? "" : this.parent.name+"|"+this.indent.to_string(), //~ " (%d) ".printf (this.indent), - of.c (ECHO.OPTION).s (">"), - of.c (ECHO.DATE).s (" %s".printf(this.uuid)) + of.c (ECHO.OPTION).s (">")/*, + of.c (ECHO.DATE).s (" %s".printf(this.uuid))*/ ); } diff --git a/src/vala/Pluie/Yaml.Document.vala b/src/vala/Pluie/Yaml.Document.vala new file mode 100644 index 0000000..c501643 --- /dev/null +++ b/src/vala/Pluie/Yaml.Document.vala @@ -0,0 +1,20 @@ +using Pluie; + +/** + * a class representing a single/pair mapping node + */ +public class Pluie.Yaml.Document : Yaml.NodeMap +{ + /** + * construct a single/pair mapping node + * @param parent the parent node + * @param indent the current indentation in node representation string + * @param name the current name (key) of sequence node + * @param data the current scalar data + */ + public Document (Yaml.Node? parent = null, int indent = 0, string? name = null, string? data = null) + { + this.standard (null, -4, NODE_TYPE.ROOT); + this.name = "PluieYamlRootNode"; + } +} diff --git a/src/vala/Pluie/Yaml.Loader.vala b/src/vala/Pluie/Yaml.Loader.vala new file mode 100644 index 0000000..b7e55cd --- /dev/null +++ b/src/vala/Pluie/Yaml.Loader.vala @@ -0,0 +1,63 @@ +using GLib; +using Gee; +using Pluie; + +/** + * a tiny Yaml Parser whose purpose is not to comply with all yaml specifications but to parse yaml configuration files + * todo improve description of what is expected + */ +public class Pluie.Yaml.Loader +{ + /** + * Scanner + */ + Yaml.Scanner scanner { public get; internal set; } + + public bool done { get; internal set; } + + /** + * Reader used to load content yaml file + */ + Io.Reader reader; + + /** + * @param path the path of file to parse + */ + public Loader (string path, bool displayFile = false, bool displayNode = false ) + { + this.reader = new Io.Reader (path); + if (displayFile) { + this.displayFile (); + } + this.scanner = new Yaml.Scanner (path); + if ((this.done = this.scanner.run()) && displayNode) { + this.get_nodes ().display_childs (); + of.state(true); + } + } + + /** + * + */ + public Yaml.NodeRoot get_nodes () + { + return this.scanner.get_nodes (); + } + + /** + * + */ + public void displayFile () + { + of.action ("Reading file", this.reader.path); + of.echo (); + while (this.reader.readable) { + of.echo ("%s %s".printf ( + of.c (ECHO.DATE ).s ("%03d |".printf (this.reader.line)), + of.c (ECHO.OPTION_SEP).s (this.reader.read ())) + ); + } + of.echo ("EOF"); + of.state (true); + } +} diff --git a/src/vala/Pluie/Yaml.Node.vala b/src/vala/Pluie/Yaml.Node.vala index e8c4f08..eb62a89 100644 --- a/src/vala/Pluie/Yaml.Node.vala +++ b/src/vala/Pluie/Yaml.Node.vala @@ -48,6 +48,12 @@ public interface Pluie.Yaml.Node : Object */ protected abstract bool remove_child (Yaml.Node child); + /** + * clone curent node + * @param name the name of clone node + */ + public abstract Yaml.Node clone_node (string? name = null); + /** * check if node has child nodes */ diff --git a/src/vala/Pluie/Yaml.NodeMap.vala b/src/vala/Pluie/Yaml.NodeMap.vala index 40a2728..08dfa32 100644 --- a/src/vala/Pluie/Yaml.NodeMap.vala +++ b/src/vala/Pluie/Yaml.NodeMap.vala @@ -57,13 +57,21 @@ public class Pluie.Yaml.NodeMap : Yaml.BaseNode, Yaml.NodeCollection /** * display childs */ - public void display_childs () + public void display_childs (bool root=true) { - of.action ("display_childs map\n"); + if (root == true) { + of.action ("display root node\n"); + } of.echo (this.to_string ()); if (this.map.size > 0) { foreach (string key in this.map.keys) { - of.echo (this.map.get(key).to_string ()); + var n = this.map.get(key); + if (n.node_type.is_mapping ()) (n as Yaml.NodeMap).display_childs (false); + else if (n.node_type.is_sequence ()) (n as Yaml.NodeSequence).display_childs (false); + else if (n.node_type.is_single_pair ()) { + of.echo (n.to_string ()); + of.echo ((n as Yaml.NodeSinglePair).scalar ().to_string ()); + } } } else { @@ -138,4 +146,20 @@ public class Pluie.Yaml.NodeMap : Yaml.BaseNode, Yaml.NodeCollection return target; } + /** + * clone current node + * @param the name of clone + */ + public override Yaml.Node clone_node (string? name = null) + { + var key = name != null ? name : this.name; + Yaml.Node clone = new Yaml.NodeMap (this.parent, this.indent, key); + foreach (string k in this.map.keys) { + var n = this.map.get(k).clone_node(); + n.parent = clone; + clone.add(n); + } + return clone; + } + } diff --git a/src/vala/Pluie/Yaml.NodeScalar.vala b/src/vala/Pluie/Yaml.NodeScalar.vala index f7ff5a7..ff95b20 100644 --- a/src/vala/Pluie/Yaml.NodeScalar.vala +++ b/src/vala/Pluie/Yaml.NodeScalar.vala @@ -17,4 +17,12 @@ public class Pluie.Yaml.NodeScalar : Yaml.BaseNode this.data = data; } + /** + * clone current node + * @param the name of clone + */ + public override Yaml.Node clone_node (string? name = null) + { + return new Yaml.NodeScalar (this.parent, this.indent, this.data); + } } diff --git a/src/vala/Pluie/Yaml.NodeSequence.vala b/src/vala/Pluie/Yaml.NodeSequence.vala index 65048f1..3f8a6f5 100644 --- a/src/vala/Pluie/Yaml.NodeSequence.vala +++ b/src/vala/Pluie/Yaml.NodeSequence.vala @@ -96,13 +96,23 @@ public class Pluie.Yaml.NodeSequence : Yaml.BaseNode, Yaml.NodeCollection /** * display childs */ - public void display_childs () + public void display_childs (bool root = true) { - of.action ("display_childs sequence\n"); + if (root) { + of.action ("display_childs sequence\n"); + } of.echo (this.to_string ()); if (this.list!= null && this.list.size > 0) { foreach (Yaml.Node child in this.list) { - of.echo (child.to_string ()); + if (child.node_type.is_mapping ()) (child as Yaml.NodeMap).display_childs (false); + else if (child.node_type.is_sequence ()) (child as Yaml.NodeSequence).display_childs (false); + else if (child.node_type.is_single_pair ()) { + of.echo (child.to_string ()); + of.echo ((child as Yaml.NodeSinglePair).scalar ().to_string ()); + } + else { + of.echo (child.to_string ()); + } } } else { @@ -114,7 +124,7 @@ public class Pluie.Yaml.NodeSequence : Yaml.BaseNode, Yaml.NodeCollection * count childnodes */ public int get_size () { - return this.list.size; + return this.list == null ? 0 : this.list.size; } /** @@ -154,4 +164,23 @@ public class Pluie.Yaml.NodeSequence : Yaml.BaseNode, Yaml.NodeCollection } return target; } + + /** + * clone current node + * @param the name of clone + */ + public override Yaml.Node clone_node (string? name = null) + { + var key = name != null ? name : this.name; + Yaml.Node clone = new Yaml.NodeSequence (this.parent, this.indent, key); + if (this.list!= null && this.list.size > 0) { + foreach (Yaml.Node child in this.list) { + var n = child.clone_node(); + n.parent = clone; + clone.add(n); + } + } + return clone; + } + } diff --git a/src/vala/Pluie/Yaml.NodeSinglePair.vala b/src/vala/Pluie/Yaml.NodeSinglePair.vala index 57a14b0..ce37bcf 100644 --- a/src/vala/Pluie/Yaml.NodeSinglePair.vala +++ b/src/vala/Pluie/Yaml.NodeSinglePair.vala @@ -31,4 +31,15 @@ public class Pluie.Yaml.NodeSinglePair : Yaml.NodeMap { return this.map["singlepair"]; } + + /** + * clone current node + * @param the name of clone + */ + public override Yaml.Node clone_node (string? name = null) + { + var key = name != null ? name : this.name; + Yaml.Node clone = new Yaml.NodeSinglePair (this.parent, this.indent, key, this.scalar ().data); + return clone; + } } diff --git a/src/vala/Pluie/Yaml.Processor.vala b/src/vala/Pluie/Yaml.Processor.vala index 844499c..099a0c7 100644 --- a/src/vala/Pluie/Yaml.Processor.vala +++ b/src/vala/Pluie/Yaml.Processor.vala @@ -1,7 +1,6 @@ using GLib; using Gee; using Pluie; -extern void yaml_parse_file(string srcPath, string destPath); /** * a tiny Yaml Parser whose purpose is not to comply with all yaml specifications but to parse yaml configuration files @@ -9,147 +8,62 @@ extern void yaml_parse_file(string srcPath, string destPath); */ public class Pluie.Yaml.Processor { - const string REG_EVENT = "^([0-9]+), ([0-9]+)(.*)$"; - const string REG_VERSION = "^, ([0-9]+), ([0-9]+)$"; - const string REG_TAG = "^, \"([^\"]*)\", \"([^\"]*)\"$"; - const string REG_ERROR = "^, \"([^\"]*)\"$"; - const string REG_SCALAR = "^, ([0-9]+), \"([^\"]*)\"$"; - const string REG_ANCHOR = "^, \"([^\"]*)\"$"; - const string REG_ALIAS = "^, \"([^\"]*)\"$"; - - /** - * enum linked to used RegExp in parse_line - */ - enum MIEVT - { - NONE, - LINE, - TYPE, - DATA - } - - /** - * enum linked to used RegExp in parse_line - */ - enum MIEVT_VERSION - { - NONE, - MAJOR, - MINOR - } - - /** - * enum linked to used RegExp in parse_line - */ - enum MIEVT_SCALAR - { - NONE, - STYLE, - DATA - } - - /** - * enum linked to used RegExp in parse_line - */ - enum MIEVT_TAG - { - NONE, - HANDLE, - SUFFIX - } - - /** - * enum linked to used RegExp in parse_line - */ - enum MIEVT_ANCHOR - { - NONE, - ID, - } - - /** - * enum linked to used RegExp in parse_line - */ - enum MIEVT_ERROR - { - NONE, - DATA, - } - /** * indicate if file has been sucessfully parsed */ public bool done; - /** - * indicate if parsing msut stop - */ - bool stop; - - /** - * the mark use to rewind line throught Io.Reader - */ - Io.StreamLineMark? mark; - - /** - * Reader used to load content yaml file - */ - Io.Reader reader; - /** * Events list */ - Gee.LinkedList events { get; internal set; } + public Gee.LinkedList events { get; internal set; } /** *Anchor map */ - Gee.HashMap anchors { get; internal set; } + Gee.HashMap anchors { get; internal set; } /** - * @param path the path of file to parse + * the root Yaml.Node */ - public Processor (string path) - { - var destPath = Path.build_filename (Environment.get_tmp_dir (), Path.get_basename(path)); - yaml_parse_file(path, destPath); - this.reader = new Io.Reader (destPath); - this.scan (); - } + public Yaml.Node root; /** - * parse a file related to specifiyed path - * @param path the path to parse + * current previous Yaml.Node */ - public bool scan (string? path = null) + Yaml.Node? prev_node; + + /** + * current parent Yaml.Node + */ + Yaml.Node? parent_node; + + /** + * current Yaml.Node + */ + Yaml.Node node; + + /** + * previous indent + */ + int prev_indent; + + /** + * + */ + public Processor () { - Dbg.in (Log.METHOD, "path:'%s'".printf (path), Log.LINE, Log.FILE); - if (path != null) { - this.reader.load (path); - } - else { - this.reader.rewind(new Io.StreamLineMark(0, 0)); - } this.events = new Gee.LinkedList(); - this.anchors = new Gee.HashMap(); - this.stop = this.done = false; - of.action ("Scanning events", path); - while (this.reader.readable) { - this.scan_line (this.reader.read ()); - } - this.process_events (); - - this.done = true; - Dbg.out (Log.METHOD, "done:%d".printf ((int)done), Log.LINE, Log.FILE); - return this.done; + this.anchors = new Gee.HashMap(); } /** * */ - private void process_events () + public void read () { of.action ("Reading events"); + EVT? prevEvent = null; foreach (Yaml.Event event in this.events) { int len = 24 - event.evtype.infos ().length; stdout.printf(" [ %s"+@" %$(len)s "+", %d, %s", event.evtype.infos (), " ", event.line, event.style != null ? event.style.to_string () : "0"); @@ -167,166 +81,136 @@ public class Pluie.Yaml.Processor } } - /** - * set event version - * @param evtdata the current data event - * @param line the current line - * @throws GLib.RegexError - */ - private void set_event_version(string evtdata, int line) throws GLib.RegexError - { - MatchInfo mi = null; - Regex reg = new Regex (REG_VERSION); - HashMap? data = null; - if (reg.match (evtdata, 0, out mi)) { - data = new HashMap(); - data.set("major", mi.fetch (MIEVT_VERSION.MAJOR)); - data.set("minor", mi.fetch (MIEVT_VERSION.MINOR)); - } - this.events.offer(new Yaml.Event(EVT.VERSION_DIRECTIVE, line, null, data)); - } - /** - * set event tag - * @param evtdata the current data event - * @param line the current line - * @throws GLib.RegexError + * */ - private void set_event_tag(string evtdata, int line) throws GLib.RegexError + private Yaml.Event? next_event (Iterator it) { - MatchInfo mi = null; - Regex reg = new Regex (REG_TAG); - HashMap? data = null; - if (reg.match (evtdata, 0, out mi)) { - data = new HashMap(); - data.set("handle", mi.fetch (MIEVT_TAG.HANDLE)); - data.set("suffix", mi.fetch (MIEVT_TAG.SUFFIX)); + Yaml.Event? evt = null; + if (it.has_next () && it.next ()) { + evt = it.get (); } - this.events.offer(new Yaml.Event(EVT.TAG, line, null, data)); + return evt; } /** - * set event version - * @param evtdata the current data event - * @param line the current line - * @throws GLib.RegexError + * */ - private void set_event_error(string evtdata, int line) throws GLib.RegexError + private Yaml.Event? get_value_key_event (Iterator it) { - MatchInfo mi = null; - Regex reg = new Regex (REG_ERROR); - HashMap? data = null; - if (reg.match (evtdata, 0, out mi)) { - data = new HashMap(); - data.set("error", mi.fetch (MIEVT_ERROR.DATA)); + Yaml.Event? evt = null; + var e = it.get (); + if (e != null && e.evtype.is_key ()) { + evt = this.next_event (it); } - this.events.offer(new Yaml.Event(EVT.NONE, line, null, data)); + return evt; } /** - * set event scalar - * @param evtdata the current data event - * @param line the current line - * @throws GLib.RegexError + * */ - private void set_event_scalar(string evtdata, int line) throws GLib.RegexError + private Yaml.Event? get_value_event (Iterator it) { - MatchInfo mi = null; - Regex reg = new Regex (REG_SCALAR); - HashMap? data = null; - int? style = null; - if (reg.match (evtdata, 0, out mi)) { - style = int.parse(mi.fetch (MIEVT_SCALAR.STYLE)); - data = new HashMap(); - data.set("data", mi.fetch (MIEVT_SCALAR.DATA)); + Yaml.Event? evt = null; + var e = it.get (); + if (e != null && e.evtype.is_value ()) { + evt = this.next_event (it); } - this.events.offer(new Yaml.Event(EVT.SCALAR, line, style, data)); + return evt; } /** - * set event anchor - * @param evtdata the current data event - * @param line the current line - * @throws GLib.RegexError + * */ - private void set_event_anchor(string evtdata, int line) throws GLib.RegexError + public bool run () { - MatchInfo mi = null; - Regex reg = new Regex (REG_ANCHOR); - HashMap? data = null; - if (reg.match (evtdata, 0, out mi)) { - data = new HashMap(); - data.set("id", mi.fetch (MIEVT_ANCHOR.ID)); - } - this.anchors.set(data.get("id"), this.events.size); - this.events.offer(new Yaml.Event(EVT.ANCHOR, line, null, data)); - } - - /** - * set event alias - * @param evtdata the current data event - * @param line the current line - * @throws GLib.RegexError - */ - private void set_event_alias(string evtdata, int line) throws GLib.RegexError - { - MatchInfo mi = null; - Regex reg = new Regex (REG_ALIAS); - HashMap? data = null; - if (reg.match (evtdata, 0, out mi)) { - data = new HashMap(); - data.set("id", mi.fetch (MIEVT_ANCHOR.ID)); - } - this.events.offer(new Yaml.Event(EVT.ALIAS, line, null, data)); - } - - /** - * parse specifiyed line - * @param data the current line - */ - private void scan_line (string? data = null) - { - Dbg.in (Log.METHOD, null, Log.LINE, Log.FILE); - if (data == null) { - this.stop = true; - return; - } - try { - MatchInfo mi = null; - Regex reg = new Regex (REG_EVENT); - if (reg.match (data, 0, out mi)) { - int line = int.parse(mi.fetch (MIEVT.LINE)); - int type = int.parse(mi.fetch (MIEVT.TYPE)); - string evtdata = mi.fetch (MIEVT.DATA); - switch(type) { - case EVT.SCALAR : - this.set_event_scalar(evtdata, line); - break; - case EVT.ANCHOR : - this.set_event_anchor(evtdata, line); - break; - case EVT.ALIAS : - this.set_event_alias(evtdata, line); - break; - case EVT.TAG : - this.set_event_tag(evtdata, line); - break; - case EVT.VERSION_DIRECTIVE : - this.set_event_version(evtdata, line); - break; - case EVT.NONE : - this.set_event_error(evtdata, line); - break; - default : - this.events.offer(new Yaml.Event((Yaml.EVT)type, line, null, null)); - break; + this.root = new Yaml.NodeRoot (); + this.prev_node = this.root; + this.parent_node = this.root; + this.prev_indent = this.root.indent; + int indent = this.root.indent +4; + EVT? prevEvent = null; + var it = this.events.iterator (); + var change = false; + string? key = null; + string? id = null; + Yaml.Event? evt; + of.action ("Processing events"); + for (var has_next = it.next (); has_next; has_next = it.next ()) { + evt = it.get (); + if (evt.evtype.is_mapping_end () || evt.evtype.is_sequence_end ()) { + indent -= 4; + this.parent_node = this.prev_node.parent != this.root ? this.prev_node.parent.parent : this.root; + this.prev_node = this.parent_node; + continue; + } + if (evt.evtype.is_entry ()) { + evt = this.next_event(it); + if (evt.evtype.is_mapping_start ()) { + key = "_%d".printf((this.parent_node as Yaml.NodeSequence).get_size()); + this.node = new Yaml.NodeMap (this.parent_node, indent, key); + key = null; + indent += 4; + change = true; } } + if (evt.evtype.is_key () && (evt = this.get_value_key_event (it)) != null) { + key = evt.data["data"]; + } + if (evt.evtype.is_value () && (evt = this.get_value_event (it)) != null) { + if (evt.evtype.is_scalar ()) { + var content = evt.data["data"]; + if (key != null) { + this.node = new Yaml.NodeSinglePair (this.parent_node, indent, key, content); + change = true; + } + } + else if (evt.evtype.is_anchor ()) { + id = evt.data["id"]; + evt = this.next_event (it); + } + else if (evt.evtype.is_alias ()) { + id = evt.data["id"]; + Yaml.Node? refnode = this.anchors.get(id); + if (refnode != null) { + this.node = refnode.clone_node (key); + this.parent_node.add (this.node); + this.prev_node = this.node; + this.prev_indent = this.prev_node.indent; + } + } + if (evt.evtype.is_mapping_start ()) { + this.node = new Yaml.NodeMap (this.parent_node, indent, key); + indent += 4; + change = true; + } + else if (evt.evtype.is_sequence_start ()) { + this.node = new Yaml.NodeSequence (this.parent_node, indent, key); + indent += 4; + change = true; + } + if (id != null) { + if (this.node != null) { + this.anchors.set(id, this.node); + } + id = null; + } + key = null; + } + if (change) { + this.parent_node.add (this.node); + if (this.node.node_type.is_collection ()) { + this.parent_node = this.node; + } + this.prev_node = this.node; + this.prev_indent = this.prev_node.indent; + this.node = null; + change = false; + } } - catch (GLib.RegexError e) { - Dbg.error (e.message, Log.METHOD, Log.LINE, Log.FILE); - } - Dbg.out (Log.METHOD, null, Log.LINE, Log.FILE); + this.done = this.root != null; + return done; } + } diff --git a/src/vala/Pluie/Yaml.Scanner.vala b/src/vala/Pluie/Yaml.Scanner.vala new file mode 100644 index 0000000..91b2886 --- /dev/null +++ b/src/vala/Pluie/Yaml.Scanner.vala @@ -0,0 +1,270 @@ +using GLib; +using Gee; +using Pluie; +extern void yaml_parse_file(string srcPath, string destPath); + +/** + * a tiny Yaml Parser whose purpose is not to comply with all yaml specifications but to parse yaml configuration files + * todo improve description of what is expected + */ +public class Pluie.Yaml.Scanner +{ + const string REG_EVENT = "^([0-9]+), ([0-9]+)(.*)$"; + const string REG_VERSION = "^, ([0-9]+), ([0-9]+)$"; + const string REG_TAG = "^, \"([^\"]*)\", \"([^\"]*)\"$"; + const string REG_ERROR = "^, \"([^\"]*)\"$"; + const string REG_SCALAR = "^, ([0-9]+), \"(.*)\"$"; + const string REG_ANCHOR = "^, \"([^\"]*)\"$"; + const string REG_ALIAS = "^, \"([^\"]*)\"$"; + + /** + * enum linked to MatchInfo REG_EVENT + */ + enum MIEVT { NONE, LINE, TYPE, DATA } + /** + * enum linked to MatchInfo REG_VERSION + */ + enum MIEVT_VERSION { NONE, MAJOR, MINOR } + /** + * enum linked to MatchInfo REG_SCALAR + */ + enum MIEVT_SCALAR { NONE, STYLE, DATA } + /** + * enum linked to MatchInfo REG_TAG + */ + enum MIEVT_TAG { NONE, HANDLE, SUFFIX } + /** + * enum linked to MatchInfo REG_ANCHOR + */ + enum MIEVT_ANCHOR { NONE, ID } + /** + * enum linked to MatchInfo REG_ERROR + */ + enum MIEVT_ERROR { NONE, DATA } + + /** + * indicate if file has been sucessfully parsed + */ + public bool done { get; internal set; } + + /** + * the mark use to rewind line throught Io.Reader + */ + Io.StreamLineMark? mark; + + /** + * Reader used to load content yaml file + */ + Io.Reader reader; + + /** + * Yaml Processor + */ + Yaml.Processor processor { get; internal set; } + + /** + * @param path the path of file to parse + */ + public Scanner (string path) + { + var destPath = Path.build_filename (Environment.get_tmp_dir (), Path.get_basename(path)); + yaml_parse_file(path, destPath); + this.reader = new Io.Reader (destPath); + } + + /** + * + */ + public Yaml.NodeRoot get_nodes () + { + return (this.processor.root as Yaml.NodeRoot); + } + + /** + * parse a file related to specifiyed path + * @param path the path to parse + */ + public bool run (string? path = null) + { + Dbg.in (Log.METHOD, "path:'%s'".printf (path), Log.LINE, Log.FILE); + if (path != null) { + this.reader.load (path); + } + else { + this.reader.rewind(new Io.StreamLineMark(0, 0)); + } + this.processor = new Yaml.Processor (); + this.done = false; + of.action ("Scanning events", path); + while (this.reader.readable) { + this.scan_event (this.reader.read ()); + } + this.done = true; + of.state (this.done); + this.done = this.done && this.processor.run (); + of.state (this.done); + Dbg.out (Log.METHOD, "done:%d".printf ((int)done), Log.LINE, Log.FILE); + return this.done; + } + + /** + * register event version + * @param evtdata the current data event + * @param line the current line + * @throws GLib.RegexError + */ + private void register_event_version(string evtdata, int line) throws GLib.RegexError + { + MatchInfo mi = null; + Regex reg = new Regex (REG_VERSION); + HashMap? data = null; + if (reg.match (evtdata, 0, out mi)) { + data = new HashMap(); + data.set("major", mi.fetch (MIEVT_VERSION.MAJOR)); + data.set("minor", mi.fetch (MIEVT_VERSION.MINOR)); + } + this.processor.events.offer(new Yaml.Event(EVT.VERSION_DIRECTIVE, line, null, data)); + } + + + /** + * register event tag + * @param evtdata the current data event + * @param line the current line + * @throws GLib.RegexError + */ + private void register_event_tag(string evtdata, int line) throws GLib.RegexError + { + MatchInfo mi = null; + Regex reg = new Regex (REG_TAG); + HashMap? data = null; + if (reg.match (evtdata, 0, out mi)) { + data = new HashMap(); + data.set("handle", mi.fetch (MIEVT_TAG.HANDLE)); + data.set("suffix", mi.fetch (MIEVT_TAG.SUFFIX)); + } + this.processor.events.offer(new Yaml.Event(EVT.TAG, line, null, data)); + } + + /** + * register event version + * @param evtdata the current data event + * @param line the current line + * @throws GLib.RegexError + */ + private void register_event_error(string evtdata, int line) throws GLib.RegexError + { + MatchInfo mi = null; + Regex reg = new Regex (REG_ERROR); + HashMap? data = null; + if (reg.match (evtdata, 0, out mi)) { + data = new HashMap(); + data.set("error", mi.fetch (MIEVT_ERROR.DATA)); + } + this.processor.events.offer(new Yaml.Event(EVT.NONE, line, null, data)); + } + + /** + * register event scalar + * @param evtdata the current data event + * @param line the current line + * @throws GLib.RegexError + */ + private void register_event_scalar(string evtdata, int line) throws GLib.RegexError + { + MatchInfo mi = null; + Regex reg = new Regex (REG_SCALAR); + HashMap? data = null; + int? style = null; + if (reg.match (evtdata, 0, out mi)) { + style = int.parse(mi.fetch (MIEVT_SCALAR.STYLE)); + data = new HashMap(); + data.set("data", mi.fetch (MIEVT_SCALAR.DATA)); + } + this.processor.events.offer(new Yaml.Event(EVT.SCALAR, line, style, data)); + } + + /** + * register event anchor + * @param evtdata the current data event + * @param line the current line + * @throws GLib.RegexError + */ + private void register_event_anchor(string evtdata, int line) throws GLib.RegexError + { + MatchInfo mi = null; + Regex reg = new Regex (REG_ANCHOR); + HashMap? data = null; + if (reg.match (evtdata, 0, out mi)) { + data = new HashMap(); + data.set("id", mi.fetch (MIEVT_ANCHOR.ID)); + } + this.processor.events.offer(new Yaml.Event(EVT.ANCHOR, line, null, data)); + } + + /** + * register event alias + * @param evtdata the current data event + * @param line the current line + * @throws GLib.RegexError + */ + private void register_event_alias(string evtdata, int line) throws GLib.RegexError + { + MatchInfo mi = null; + Regex reg = new Regex (REG_ALIAS); + HashMap? data = null; + if (reg.match (evtdata, 0, out mi)) { + data = new HashMap(); + data.set("id", mi.fetch (MIEVT_ANCHOR.ID)); + } + this.processor.events.offer(new Yaml.Event(EVT.ALIAS, line, null, data)); + } + + /** + * parse specifiyed line + * @param data the current line + */ + private void scan_event (string? data = null) + { + Dbg.in (Log.METHOD, null, Log.LINE, Log.FILE); + if (data == null) { + return; + } + try { + MatchInfo mi = null; + Regex reg = new Regex (REG_EVENT); + if (reg.match (data, 0, out mi)) { + int line = int.parse(mi.fetch (MIEVT.LINE)); + int type = int.parse(mi.fetch (MIEVT.TYPE)); + string evtdata = mi.fetch (MIEVT.DATA); + switch(type) { + case EVT.SCALAR : + this.register_event_scalar(evtdata, line); + break; + case EVT.ANCHOR : + this.register_event_anchor(evtdata, line); + break; + case EVT.ALIAS : + this.register_event_alias(evtdata, line); + break; + case EVT.TAG : + this.register_event_tag(evtdata, line); + break; + case EVT.VERSION_DIRECTIVE : + this.register_event_version(evtdata, line); + break; + case EVT.NONE : + this.register_event_error(evtdata, line); + break; + default : + this.processor.events.offer(new Yaml.Event((Yaml.EVT)type, line, null, null)); + break; + } + } + } + catch (GLib.RegexError e) { + Dbg.error (e.message, Log.METHOD, Log.LINE, Log.FILE); + } + Dbg.out (Log.METHOD, null, Log.LINE, Log.FILE); + } +} diff --git a/src/vala/Pluie/Yaml.global.vala b/src/vala/Pluie/Yaml.global.vala index 2de9c08..f81313a 100644 --- a/src/vala/Pluie/Yaml.global.vala +++ b/src/vala/Pluie/Yaml.global.vala @@ -47,12 +47,93 @@ namespace Pluie SCALAR; /** - *@return infos related to EVT + * @return infos related to EVT */ public string infos () { return this.to_string().substring("PLUIE_YAML_".length); } + + /** + * @return event is key + */ + public bool is_key () + { + return this == EVT.KEY; + } + + /** + * @return event is anchor + */ + public bool is_anchor () + { + return this == EVT.ANCHOR; + } + + /** + * @return event is alias + */ + public bool is_alias () + { + return this == EVT.ALIAS; + } + + /** + * @return event is key + */ + public bool is_value () + { + return this == EVT.VALUE; + } + + /** + * @return event is scalar + */ + public bool is_scalar () + { + return this == EVT.SCALAR; + } + + /** + * @return event is mapping start event + */ + public bool is_mapping_start () + { + return this == EVT.BLOCK_MAPPING_START || this == EVT.FLOW_MAPPING_START; + } + + /** + * @return event is sequence start event + */ + public bool is_sequence_start () + { + return this == EVT.BLOCK_SEQUENCE_START || this == EVT.FLOW_SEQUENCE_START; + } + + /** + * @return event is sequence end event + */ + public bool is_sequence_end () + { + return this == EVT.BLOCK_END || this == EVT.FLOW_SEQUENCE_END; + } + + /** + * @return event is sequence entry event + */ + public bool is_entry () + { + return this == EVT.BLOCK_ENTRY || this == EVT.FLOW_ENTRY; + } + + /** + * @return event is mapping end event + */ + public bool is_mapping_end () + { + return this == EVT.BLOCK_END; + } + } /** @@ -116,7 +197,7 @@ namespace Pluie /** * @return if current NODE_TYPE match a scalar node */ - public bool is_scallar () + public bool is_scalar () { return this == SCALAR; }