src/parse.c devel branch commit 79cae53525

unstable documentation:
  links towards reference texts describing how gem-graph operates
        (and why all the parsed items are useful, compulsory or optional) missing.
  links towards description of the gem-graph model structure (and its XML implementation) missing.

Warning about the word 'node': possible confusion between:
-  the xml tree 'nodes' used in the model xml file and
-  the gem-graph 'nodes' that are local space units used in the geometric space representation
This commit is contained in:
Jean Sirmai 2024-11-01 14:35:31 +01:00
parent 354a53eefb
commit 8003509a72
Signed by: jean
GPG Key ID: FB3115C340E057E3
4 changed files with 287 additions and 131 deletions

View File

@ -1,21 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<gem-graph-model version="0.2.1">
<identity>
<name>Modèle de test</name>
<owner>Léontine Patinette</owner>
<owner_id>2</owner_id>
<date>1630000000</date>
<identity !-- TODO define a key >
<name>model name </name>
<owner>owner</owner>
<owner_id>owner_id</owner_id>
<date>2021</date>
<version>1.0</version>
<g_ref id="text" date="date" author="author" lang="en">Ref</g_ref>
<g_ref id="Cause" date="1630000000" author="toujours" lang="en">Ref</g_ref>
</identity>
<parameters id="texte" date="0" author="Léontine Trottine.">
<parameters id="text" date="date" author="author">
<simulation>
<max_thread>0</max_thread>
<max_cycles>9</max_cycles>
@ -42,8 +38,8 @@
<!-- Model objects definition -->
<objects id="only dimers in this model" date="1630000000" author="inanimés,... âme ?">
<object id="a dimer" date="1630000000" author="Alphonse">
<objects id="only dimers in this model" date="date" author="author">
<object id="a dimer" date="date" author="author">
<arrow site="1" weight="1" x="0"/>
<arrow site="2" weight="1" x="1"/>
</object>
@ -53,8 +49,8 @@
<!-- Saved space description (initial space is sequence 0) -->
<savedstates id="texte" date="1630000000" author="Qui courrait dans l'herbe'">
<state id="initial" date="1630000000" author="Une souris verte">
<savedstates id="texte" date="date" author="author">
<state id="initial" date="date" author="author">
<arrow site="0" weight="1" x="0"/>
<arrow site="1" weight="1" x="1"/>
<arrow site="0" weight="1" x="10"/>
@ -73,7 +69,7 @@
<!-- In this version : <xs:sequence minOccurs="0" maxOccurs="unbounded">-->
<!-- Transitions should be edited by hand and written in a 'human-readable' format -->
<conditions id="random walk of dimers" date="1630000000" author="Zazard le lézard !">
<conditions id="random walk of dimers" date="date" author="author">
<condition site="1" weight="1" node_id="1" parent="0" x="0"/>
<condition site="2" weight="1" node_id="2" parent="1" x="1"/>
@ -93,7 +89,7 @@
<transitions>
<transition id="move_a_dimer_to_east" date="1630000000" author="Pas moi..."
<transition id="move_a_dimer_to_east" date="date" author="author"
parent="3" probability="1">
<arrow site="1" weight="0" x="0"/>
@ -103,7 +99,7 @@
</transition>
<transition id="move_a_dimer_to_west" date="1630000000" author="Ni moi !"
<transition id="move_a_dimer_to_west" date="date" author="author"
parent="4" probability="1">
<arrow site="1" weight="0" x="0"/>

View File

@ -1,25 +1,32 @@
/*
* @file
*
* Gem-graph main file
*
* Gem-graph OpenGL experiments
*
* Desc: Base header
*
* Copyright (C) 2023 Arthur Menges <arthur.menges@a-lec.org>
* Copyright (C) 2023 Adrien Bourmault <neox@a-lec.org>
*
* This file is part of Gem-graph.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* @cond LICENSE
* Copyright © 2021 Libre en Communs <contact@a-lec.org>
* Copyright © 2021-2024 Adrien Bourmault <neox@a-lec.org>
* Copyright © 2021-2024 Jean Sirmai <jean@a-lec.org>
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* @endcond
*/
#pragma once
@ -58,10 +65,9 @@ static inline char *read_file(char *filename); // < used by : init.c
/*
* char *read_file(char *filename) reads a file from filename into a provided buffer
*
* @param filename, file name
* contents, target ptr
* @param filename
*
* @return void
* @return contents
*/
static inline char *read_file(char *filename)
{

View File

@ -26,18 +26,19 @@
#include <unistd.h>
#include "base.h"
bool model_init(const char *content, size_t length, const char *basename);
bool model_shutdown(void);
bool parse_model_init(const char *content, size_t length, const char *basename);
bool parse_model_shutdown(void);
char model_get_dim(void);
long model_get_dim_value(const char *axis);
char model_get_multiplicity(void);
bool model_get_next_state(char *new_state_id);
bool model_get_next_arrow(struct arrow_t *new_arrow,
char parse_model_get_dim(void);
long parse_model_get_dim_value(const char *axis);
char parse_model_get_multiplicity(void);
bool parse_model_get_next_space_state(char *new_state_id);
bool parse_model_get_next_arrow(struct arrow_t *new_arrow,
const char *state_id,
char dimension);
long model_get_state_arrows_count(const char *state_id);
bool model_get_next_arrow(struct arrow_t *new_arrow,
long parse_model_get_state_arrows_count(const char *state_id);
bool parse_model_get_next_arrow(struct arrow_t *new_arrow,
const char *state_id,
char dimension);
void parse_model_test();

View File

@ -1,6 +1,53 @@
/**
* @file
* Phantom docs (TODO)
*
* XML Gem-graph model parsing functions
*
* -----------------------------------------------------------------------------
*
* @warning about the word '*node*'
*
* A possible confusion between the xml tree '*nodes*' used in the model xml file
* and the gem-graph '*nodes*' that are local space units used in the geometric
* space representation could make this code difficult to understand;
*
* The local name 'node' used alone should be avoided.
*
* In this version, this ambiguity is resolved by using the name "cur_node"
* (current node) for the the xml 'nodes' processed by the parser.
*
* -----------------------------------------------------------------------------
*
* unstable documentation: TODO
* - a link towards reference text describing how gem-graph operates and why
* all the parsed items (compulsory or optional) are useful is missing.
* - a link towards the description of the gem-graph model structure (and its XML
* implementation) is missing.
*
* List of the gem-graph concepts and names (version="0.2.1") parsed here:
* - identity,
* - parameters
* - objects
* - global space (as opposed to local space or working area)
* - dimension (of the space), size
* - topology and limits (of the space): torus, limited area,...
* - <savedstates> (state of the global space)
* - conditions
* - transitions
* - arrow
* - arrow address (site, weight, x, y, z)
* - site
* - node (local space units of the geometric graph)
* - multiplicity (number of sites in each state unit ('cell')
* .
*
* @see space doc (including arrow doc) (TODO)
*
* -----------------------------------------------------------------------------
*
* This file is inherited from a previous development of the Gem-graph server
* then client, in the devel branch commit
* 79cae535258d557e29592a45fbdf426149505738
*
* This file is part of Gem-graph.
*
@ -24,36 +71,59 @@
* @endcond
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include <limits.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/xmlreader.h> // http://xmlsoft.org/examples/#parse1.c
// https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/general.html
#include "../include/base.h"
#define READ_SITE 1 << 0
#define READ_WEIGHT 1 << 1
#define READ_X 1 << 2
#define READ_Y 1 << 3
#define READ_Z 1 << 4
#define SUCCESSFUL_READ_ARROW_X (READ_SITE | READ_WEIGHT | READ_X)
#define SUCCESSFUL_READ_ARROW_XY (READ_SITE | READ_WEIGHT | READ_X | READ_Y)
#define SUCCESSFUL_READ_ARROW_XYZ (READ_SITE | READ_WEIGHT | READ_X | READ_Y | READ_Z)
#define READ_SITE 1 << 0 /**< arrow address = (site, weight, x, y, z) @see arrow doc */
#define READ_WEIGHT 1 << 1 /**< arrow address = (site, weight, x, y, z) @see arrow doc */
#define READ_X 1 << 2 /**< arrow address = (site, weight, x, y, z) @see arrow doc */
#define READ_Y 1 << 3 /**< arrow address = (site, weight, x, y, z) @see arrow doc */
#define READ_Z 1 << 4 /**< arrow address = (site, weight, x, y, z) @see arrow doc */
/** unstable doc */
#define SUCCESSFUL_READ_ARROW_X (READ_SITE | READ_WEIGHT | READ_X) /**< flag 1D */
/** unstable doc */
#define SUCCESSFUL_READ_ARROW_XY (READ_SITE | READ_WEIGHT | READ_X | READ_Y) /**< flag 2D */
/** unstable doc */
#define SUCCESSFUL_READ_ARROW_XYZ (READ_SITE | READ_WEIGHT | READ_X | READ_Y | READ_Z) /**< flag 3D */
static xmlDocPtr model;
static xmlHashTablePtr model_hashtable;
static xmlHashTablePtr parse_model_hashtable;
bool model_init(const char *content, size_t length, const char *basename)
/**
* unstable doc
*
* a debug function
*
* @callgraph parse_model_init()
* @callgraph lseek()
*/
void parse_model_test()
{
xmlNode *node;
/*
char *content;
char *basename = "data/dimers random walk.xml";
size_t length = lseek(*basename, length, *xxx);
parse_model_init (content, length, "/truc/fichier.xml");
*/
}
/**
* unstable doc
*
* @param *content
* @param length
* @param *basename
*
* @returns bool
*/
bool parse_model_init(const char *content, size_t length, const char *basename)
{
xmlNode *cur_node;
/**
* this initialize the library and check potential ABI mismatches
* between the version it was compiled for and the actual shared
* library used.
@ -66,23 +136,23 @@ bool model_init(const char *content, size_t length, const char *basename)
return false;
}
node = xmlDocGetRootElement(model);
cur_node = xmlDocGetRootElement(model);
if (node == NULL) {
if (cur_node == NULL) {
g_printerr("Empty XML model !\n");
xmlFreeDoc(model);
return false;
}
if (xmlStrcmp(node->name, (xmlChar *) "gem-graph-model")) {
if (xmlStrcmp(cur_node->name, (xmlChar *) "gem-graph-model")) {
g_printerr("document of the wrong type, root node != gem-graph-model\n");
xmlFreeDoc(model);
return false;
}
model_hashtable = xmlHashCreate(0);
parse_model_hashtable = xmlHashCreate(0);
if (model_hashtable == NULL) {
if (parse_model_hashtable == NULL) {
g_printerr("Can't create model hash table !\n");
xmlFreeDoc(model);
return false;
@ -91,10 +161,15 @@ bool model_init(const char *content, size_t length, const char *basename)
return true;
}
bool model_shutdown(void)
/**
* unstable doc
*
* @returns bool
*/
bool parse_model_shutdown(void)
{
xmlFreeDoc(model);
xmlHashFree(model_hashtable, NULL);
xmlHashFree(parse_model_hashtable, NULL);
// Cleanup function for the XML library
xmlCleanupParser();
@ -107,15 +182,22 @@ bool model_shutdown(void)
/******************************************************************************/
static inline xmlNodePtr getNextChild(xmlNodePtr node, xmlChar *last)
/**
* static doc is ignored by doxygen / consider local helpfulness
*/
static inline xmlNodePtr getNextChild(xmlNodePtr cur_node, xmlChar *last)
{
while (node != NULL && xmlStrcmp(node->name, last)) {
// //printf(" <>--- line n°%lu <%s>\n", xmlGetLineNo(node), node->name);
node = node->next;
while (cur_node != NULL && xmlStrcmp(cur_node->name, last)) {
// printf(" <>--- line n°%lu <%s>\n", xmlGetLineNo(cur_node),
// cur_node->name);
cur_node = cur_node->next;
}
return node;
return cur_node;
}
/**
* static doc is ignored by doxygen / consider local helpfulness
*/
static inline xmlChar* splitStrAtSlash(xmlChar *toSplit)
{
toSplit = (xmlChar *)xmlStrchr(toSplit, '/');
@ -123,17 +205,24 @@ static inline xmlChar* splitStrAtSlash(xmlChar *toSplit)
return toSplit;
}
/**
* static doc is ignored by doxygen / consider local helpfulness
*/
static inline xmlChar* getFirstTag(xmlChar *path)
{
xmlChar *preop = path;
path = (xmlChar *)xmlStrchr(path, '/');
path = xmlStrsub (path, 1, xmlStrlen(path));
//printf("%s = %s + / + %s\n", preop, xmlStrsub (preop, 0, xmlStrlen(preop) - xmlStrlen(path) - 1), path);
// printf("%s = %s + / + %s\n", preop,
// xmlStrsub (preop, 0, xmlStrlen(preop) - xmlStrlen(path) - 1), path);
return xmlStrsub (preop, 0, xmlStrlen(preop) - xmlStrlen(path) - 1);
}
/**
* static doc is ignored by doxygen / consider local helpfulness
*/
static inline xmlChar* getLastTag(xmlChar *path)
{
while ((ulong)xmlStrchr (path, '/'))
@ -145,42 +234,45 @@ static inline xmlChar* getLastTag(xmlChar *path)
/******************************************************************************/
static xmlNodePtr model_get_node(xmlChar *path)
/**
* static doc is ignored by doxygen / consider local helpfulness
*/
static xmlNodePtr parse_model_get_node(xmlChar *path)
{
xmlNodePtr node;
xmlNodePtr cur_node;
xmlChar *extrait;
xmlChar *reste, *last;
// Lookup for node from path in hash table
node = xmlHashLookup(model_hashtable, path);
// Lookup for cur_node from path in hash table
cur_node = xmlHashLookup(parse_model_hashtable, path);
// Found a node in hash table
if (node) {
return node;
// Found a cur_node in hash table
if (cur_node) {
return cur_node;
// no node found in hash table
// no cur_node found in hash table
} else {
reste = path;
last = getLastTag(reste);
node = xmlDocGetRootElement(model);
cur_node = xmlDocGetRootElement(model);
while (xmlStrchr (reste, '/')) {
extrait = getFirstTag(reste);
reste = splitStrAtSlash((xmlChar *)reste);
node = node->xmlChildrenNode;
node = getNextChild(node, extrait);
cur_node = cur_node->xmlChildrenNode;
cur_node = getNextChild(cur_node, extrait);
}
if(node && xmlStrcmp(node->name, last)) {
node = node->xmlChildrenNode;
if(cur_node && xmlStrcmp(cur_node->name, last)) {
cur_node = cur_node->xmlChildrenNode;
while (node && xmlStrcmp(node->name, last)) {
node = node->next;
while (cur_node && xmlStrcmp(cur_node->name, last)) {
cur_node = cur_node->next;
}
xmlHashAddEntry (model_hashtable, path, node);
xmlHashAddEntry (parse_model_hashtable, path, cur_node);
}
return node;
return cur_node;
}
@ -189,17 +281,20 @@ static xmlNodePtr model_get_node(xmlChar *path)
/******************************************************************************/
static inline long model_get_node_long_attrib(xmlNodePtr node, char *id)
/**
* static doc is ignored by doxygen / consider local helpfulness
*/
static inline long parse_model_get_node_long_attrib(xmlNodePtr cur_node, char *id)
{
xmlAttr *attribute;
xmlChar* value;
long ret_value;
if (node && node->properties) {
attribute = node->properties;
if (cur_node && cur_node->properties) {
attribute = cur_node->properties;
while(attribute && attribute->name && attribute->children) {
if (!xmlStrcmp(attribute->name, (const xmlChar *)id)) {
value = xmlNodeListGetString(node->doc, attribute->children, 1);
value = xmlNodeListGetString(cur_node->doc, attribute->children, 1);
ret_value = strtol((char *)value, NULL, 0);
xmlFree(value);
return ret_value;
@ -210,18 +305,21 @@ static inline long model_get_node_long_attrib(xmlNodePtr node, char *id)
return 0;
}
static inline bool model_get_node_str_attrib(xmlNodePtr node,
/**
* static doc is ignored by doxygen / consider local helpfulness
*/
static inline bool parse_model_get_node_str_attrib(xmlNodePtr cur_node,
char *id,
char *dest)
{
xmlAttr *attribute;
xmlChar* value;
if (node && node->properties) {
attribute = node->properties;
if (cur_node && cur_node->properties) {
attribute = cur_node->properties;
while(attribute && attribute->name && attribute->children) {
if (!xmlStrcmp(attribute->name, (const xmlChar *)id)) {
value = xmlNodeListGetString(node->doc, attribute->children, 1);
value = xmlNodeListGetString(cur_node->doc, attribute->children, 1);
strcpy(dest, (char *)value);
xmlFree(value);
return true;
@ -234,45 +332,81 @@ static inline bool model_get_node_str_attrib(xmlNodePtr node,
/******************************************************************************/
char model_get_dim(void)
/**
* unstable doc
*
* 'dim' means here the number of dimensions (x,y,z) of this model space
* @see a reference text explaining that models of any dimension (> 0) can be
* used by gem-graph
*
* @returns 0, 1, 2, or 3
*/
char parse_model_get_dim(void)
{
// xmlAttr *attribute;
// xmlChar* value;
xmlNodePtr node = model_get_node(
xmlNodePtr cur_node = parse_model_get_node(
(xmlChar *)"parameters/space-param/dimension");
if (xmlHasProp (node, (xmlChar *) "z")) return 3;
if (xmlHasProp (node, (xmlChar *) "y")) return 2;
if (xmlHasProp (node, (xmlChar *) "x")) return 1;
if (xmlHasProp (cur_node, (xmlChar *) "z")) return 3;
if (xmlHasProp (cur_node, (xmlChar *) "y")) return 2;
if (xmlHasProp (cur_node, (xmlChar *) "x")) return 1;
return 0;
}
long model_get_dim_value(const char *axis)
/**
* unstable doc
*
* 'dim' means here the number of dimensions (x,y,z) of this model space
* @see a reference text explaining that models of any dimension (> 0) can be
* used by gem-graph
*
* @param *axis
*
* @returns long
*/
long parse_model_get_dim_value(const char *axis)
{
// xmlAttr *attribute;
// xmlChar *value;
// long ret_value;
xmlNodePtr node = model_get_node(
xmlNodePtr cur_node = parse_model_get_node(
(xmlChar *)"parameters/space-param/dimension");
return model_get_node_long_attrib(node, (char *)axis);
return parse_model_get_node_long_attrib(cur_node, (char *)axis);
}
char model_get_multiplicity(void)
/**
* unstable doc
*
* 'multiplicity'
* @see arrow doc
*/
char parse_model_get_multiplicity(void)
{
// xmlAttr *attribute;
// xmlChar* value;
xmlNodePtr node = model_get_node(
xmlNodePtr cur_node = parse_model_get_node(
(xmlChar *)"parameters/space-param/site_multiplicity");
if (node->children)
if (node->children->content)
return (char)strtol((char *)node->children->content, NULL, 0);
if (cur_node->children)
if (cur_node->children->content)
return (char)strtol((char *)cur_node->children->content, NULL, 0);
return 0;
}
bool model_get_next_state(char *new_state_id)
/**
* unstable doc
*
* next *state* is one of the possible states of the global space.
*
* @see a reference text explaining that the gem-graph server calculates successive
* states of a Markov chain from the initial state;
*
* @param *new_state_id
*
* @returns boolean
*/
bool parse_model_get_next_space_state(char *new_state_id)
{
static xmlNodePtr cur_node = NULL;
// xmlAttr *attribute;
@ -280,7 +414,7 @@ bool model_get_next_state(char *new_state_id)
if (cur_node == NULL) {
// Get first state
cur_node = model_get_node((xmlChar *)"savedstates/state");
cur_node = parse_model_get_node((xmlChar *)"savedstates/state");
} else {
// Get next state
@ -291,14 +425,21 @@ bool model_get_next_state(char *new_state_id)
}
// Lookup in properties
if (model_get_node_str_attrib(cur_node, "id", new_state_id))
if (parse_model_get_node_str_attrib(cur_node, "id", new_state_id))
return true;
cur_node = NULL;
return false;
}
long model_get_state_arrows_count(const char *state_id)
/**
* unstable doc
*
* @param *state_id
*
* @returns long
*/
long parse_model_get_state_arrows_count(const char *state_id)
{
xmlNodePtr cur_node = NULL;
// xmlAttr *attribute;
@ -312,14 +453,14 @@ long model_get_state_arrows_count(const char *state_id)
assert(state_id);
// Get first state node
cur_node = model_get_node((xmlChar *)"savedstates/state");
cur_node = parse_model_get_node((xmlChar *)"savedstates/state");
// Lookup in properties
while (cur_node && cur_node->properties) {
// attribute = cur_node->properties; < usage ?
// Look for the id attribute
if (model_get_node_str_attrib(cur_node, "id", (char *)&temp_char)) {
if (parse_model_get_node_str_attrib(cur_node, "id", (char *)&temp_char)) {
if (!xmlStrcmp((const xmlChar *)temp_char, (const xmlChar *)state_id)) {
found = true;
break;
@ -349,7 +490,19 @@ long model_get_state_arrows_count(const char *state_id)
return value;
}
bool model_get_next_arrow(struct arrow_t *new_arrow,
/**
* unstable doc
*
* @see A reference text explaining that the list parsed here describes the
* arrows repartition in the initial state of the space.
*
* @param *new_arrow
* @param *state_id
* @param *dimension
*
* @returns bool
*/
bool parse_model_get_next_arrow(struct arrow_t *new_arrow,
const char *state_id,
char dimension)
{
@ -367,14 +520,14 @@ bool model_get_next_arrow(struct arrow_t *new_arrow,
if (cur_node == NULL) {
// Get first state node
cur_node = model_get_node((xmlChar *)"savedstates/state");
cur_node = parse_model_get_node((xmlChar *)"savedstates/state");
// Lookup in properties
while (cur_node && cur_node->properties) {
attribute = cur_node->properties;
// Look for the id attribute
if (model_get_node_str_attrib(cur_node, "id", (char *)&temp_char)) {
if (parse_model_get_node_str_attrib(cur_node, "id", (char *)&temp_char)) {
if (!xmlStrcmp((xmlChar *)temp_char, (const xmlChar *)state_id)) {
found = true;
break;