From ea20d756deab710bbcd332420150670fb2db789d Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Tue, 1 Nov 2011 23:37:28 +0300 Subject: [PATCH 01/84] first commit --- README | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 README diff --git a/README b/README new file mode 100644 index 00000000..e69de29b From 26fb814fb8c81f0b14d1944fb260783e8808b42d Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Wed, 12 Sep 2012 01:02:35 +0300 Subject: [PATCH 02/84] added main sub-modules --- .gitmodules | 9 +++++++++ sc-machine | 1 + sc-web | 1 + tools/kbe | 1 + 4 files changed, 12 insertions(+) create mode 100644 .gitmodules create mode 160000 sc-machine create mode 160000 sc-web create mode 160000 tools/kbe diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..0e5796b5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "tools/kbe"] + path = tools/kbe + url = git@github.com:deniskoronchik/kbe.git +[submodule "sc-machine"] + path = sc-machine + url = git@github.com:deniskoronchik/sc-machine.git +[submodule "sc-web"] + path = sc-web + url = git@github.com:deniskoronchik/sc-web.git diff --git a/sc-machine b/sc-machine new file mode 160000 index 00000000..eae1520f --- /dev/null +++ b/sc-machine @@ -0,0 +1 @@ +Subproject commit eae1520f1905ca5c2350b1a687cd433f1752f84d diff --git a/sc-web b/sc-web new file mode 160000 index 00000000..cbbf5ed4 --- /dev/null +++ b/sc-web @@ -0,0 +1 @@ +Subproject commit cbbf5ed4030205cb19c0de7c1268fb21a26dfddf diff --git a/tools/kbe b/tools/kbe new file mode 160000 index 00000000..e9200408 --- /dev/null +++ b/tools/kbe @@ -0,0 +1 @@ +Subproject commit e9200408ac0966850b84002309e78e344479717d From 760ccef27b9f61f55ad76973fc9b614daae2e71c Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Fri, 23 Nov 2012 01:00:34 +0300 Subject: [PATCH 03/84] added scs-converter tool --- tools/scs-converter/converter.py | 412 +++++++++++++++++++++++++++ tools/scs-converter/parser.py | 436 +++++++++++++++++++++++++++++ tools/scs-converter/tests/test.scs | 13 + 3 files changed, 861 insertions(+) create mode 100644 tools/scs-converter/converter.py create mode 100644 tools/scs-converter/parser.py create mode 100644 tools/scs-converter/tests/test.scs diff --git a/tools/scs-converter/converter.py b/tools/scs-converter/converter.py new file mode 100644 index 00000000..ab643f7e --- /dev/null +++ b/tools/scs-converter/converter.py @@ -0,0 +1,412 @@ +# -*- coding: utf-8 -*- +import os, sys, shutil +import codecs +import re, sys, traceback +from pyparsing import Word, Literal, Forward, Regex, Group, ZeroOrMore, SkipTo, ParserElement +from pyparsing import OneOrMore, srange, Keyword, QuotedString, ParseResults, Optional, cStyleComment, ParseException +import parser +from sre_parse import parse_template + +ParserElement.enablePackrat() + +reKeyword = r'/!\*\s*keyword:\s*([a-zA-Z0-9_]+)\s*\*/' + +arc_id = 0 + +mirror_connectors = [ + u'<', + u'<..', + u'<-', + u'<=', + u'<|-', + u'" : "sc_arc_common", + u"<" : "sc_arc_common", + u"->": "sc_arc_main", + u"<-": "sc_arc_main", + u"<>": "sc_edge", + u"..>": "sc_arc_access", + u"<..": "sc_arc_access", + u'<=>': "sc_edge", + u'_<=>': "sc_edge", + u'=>': "sc_arc_common", + u'<=': "sc_arc_common", + u'=>': "sc_arc_common", + u'<=': "sc_arc_common", + u'_->': "sc_arc_access", + u'_<-': "sc_arc_access", + u'-|>': "sc_arc_access", + u'_-|>': "sc_arc_access", + u'<|-': "sc_arc_access", + u'_<|-': "sc_arc_access", + u'-/>': "sc_arc_access", + u'_-/>': "sc_arc_access", + u'': "sc_arc_access", + u'_~>': "sc_arc_access", + u'<~': "sc_arc_access", + u'_<~': "sc_arc_access", + u'~|>': "sc_arc_access", + u'_~|>': "sc_arc_access", + u'<|~': "sc_arc_access", + u'_<|~': "sc_arc_access", + u'~/>': "sc_arc_access", + u'_~/>': "sc_arc_access", + u'': "sc_edge_const", + u'_<=>': "sc_edge_var", + u'=>': "sc_arc_common_const", + u'<=': "sc_arc_common_const", + u'=>': "sc_arc_common_var", + u'<=': "sc_arc_common_var", + u'_->': "sc_arc_access_var_pos_perm", + u'_<-': "sc_arc_access_var_pos_perm", + u'-|>': "sc_arc_access_const_neg_perm", + u'_-|>': "sc_arc_access_var_neg_perm", + u'<|-': "sc_arc_access_const_neg_perm", + u'_<|-': "sc_arc_access_var_neg_perm", + u'-/>': "sc_arc_access_const_fuz_perm", + u'_-/>': "sc_arc_access_var_fuz_perm", + u'': "sc_arc_access_const_pos_temp", + u'_~>': "sc_arc_access_var_pos_temp", + u'<~': "sc_arc_access_const_pos_temp", + u'_<~': "sc_arc_access_var_pos_temp", + u'~|>': "sc_arc_access_const_neg_temp", + u'_~|>': "sc_arc_access_var_neg_temp", + u'<|~': "sc_arc_access_const_neg_temp", + u'_<|~': "sc_arc_access_var_neg_temp", + u'~/>': "sc_arc_access_const_fuz_temp", + u'_~/>': "sc_arc_access_var_fuz_temp", + u'', False), res, False) + + return res + + def process_arc_connector(self, connector): + assert connector != "=" + global arc_id + + # todo fixme + res = "sc_arc_common#%d" % arc_id + arc_id += 1 + + return res + + def resolve_identifier(self, group): + """Resolves identifiers for different groups + """ + + key = str(group) + if self.aliases.has_key(key): + return self.aliases[key] + + alias = str(group) + if isinstance(group, parser.OSetGroup): + alias = self.generate_oset_idtf() + elif isinstance(group, parser.SetGroup): + alias = self.generate_set_idtf() + + self.aliases[key] = alias + + return alias + + def append_synonyms(self, idtf1, idtf2): + """Appends two identifiers as synonyms into map + """ + pass + + def check_predicate_mirror(self, predicate): + + return (predicate in mirror_connectors) + + def append_sentence(self, subject, predicate, object, isMirrored): + """Appends new scs-level 1 sentence into list + """ + if not isMirrored: + self.triples.append((subject, predicate, object)) + else: + self.triples.append((object, predicate, subject)) + + # --------------------------------------- + def processSimpleIdentifierGroup(self, group): + return self.resolve_identifier(group) + + def processUrlGroup(self, group): + pass + + def processKeywordGroup(self, group): + pass + + def processSimpleSentenceGroup(self, group): + """Process scs-level 1 sentences + """ + self.append_sentence(group.subject, group.predicate, group.object, False); + + def processIdtfWithIntGroup(self, group): + """Process identifier with internal sentence group + """ + subject_idtf = self.resolve_identifier(group.idtf) + self.parse_tree(group.idtf) + + internal_list = group.internal + if internal_list is not None: + for sentence in internal_list.sentences: + + for obj in sentence.object: + object_idtf = self.resolve_identifier(obj) + attributes = sentence.attrs + arc_idtf = self.generate_arc_idtf(sentence.predicate) + + self.append_sentence(subject_idtf, arc_idtf, object_idtf, self.check_predicate_mirror(sentence.predicate)) + + # write attributes + for attr in attributes: + attr_idtf = self.resolve_identifier(attr) + self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) + + + def processInternalGroup(self, group): + pass + + def processInternalListGroup(self, group): + pass + + def processTripleGroup(self, group): + pass + + def processAliasGroup(self, group): + """Just resolve identifier for alias + """ + return self.resolve_identifier(group) + + def processContentGroup(self, group): + pass + + def processSetGroup(self, group): + """Process set + """ + idtf = self.resolve_identifier(group) + for item in group.items: + attributes = item[0] + object = item[1] + + self.parse_tree(object) + arc_idtf = self.generate_arc_idtf('->') + self.append_sentence(idtf, arc_idtf, self.resolve_identifier(object), False) + + for attr in attributes: + attr_idtf = self.resolve_identifier(attr) + self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) + + + def processOSetGroup(self, group): + """Process ordered tree + """ + idtf = self.resolve_identifier(group) + item_count = 0 + for item in group.items: + attributes = item[0] + object = item[1] + item_count += 1 + + self.parse_tree(object) + arc_idtf = self.generate_arc_idtf('->') + self.append_sentence(idtf, arc_idtf, self.resolve_identifier(object), False) + # add order attribute + self.append_sentence("%d_" % item_count, self.generate_arc_idtf('->'), arc_idtf, False) + + for attr in attributes: + attr_idtf = self.resolve_identifier(attr) + self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), object, False) + + def processSentenceGroup(self, group): + """Process sentence for scs-levels 2-6 + """ + subject = group.subject + predicate = group.predicate + attributes = group.attrs + objects = group.object + + self.parse_tree(subject) + for obj in objects: + # process object + self.parse_tree(obj) + # resolve object identifier + obj_idtf = self.resolve_identifier(obj) + + # connect subject with object + arc_idtf = self.generate_arc_idtf(predicate) + self.append_sentence(subject, arc_idtf, obj_idtf, self.check_predicate_mirror(predicate)) + + # connect attributes + for attr in attributes: + self.parse_tree(attr) + attr_idtf = self.resolve_identifier(attr) + self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) + + def processSynonymGroup(self, group): + pass + + + def get_string_value(self, element): + """Returns string representation of specified element + from parser results + """ + pass + + def parse_skip(self, group): + pass + + def parse_simple_sentence(self, group): + pass + + + # ----------------------------------------- + def parse_tree(self, tree): + """Parse results tree + """ + + if isinstance(tree, ParseResults): + for item in tree: + self.parse_tree(item) + else: + self.group_processors[tree.__class__](tree) + + + def parse_directory(self, path): + """Parse specified directory + """ + for root, dirs, files in os.walk(path): + print root + for f in files: + + # skip none scs files + base, ext = os.path.splitext(f) + if ext != '.scs': + continue + + file_path = os.path.join(root, f) + + self.triples.append('/* ------' + file_path + ' ----- */') + + # parse file + try: + print "Parse %s" % file_path + fh = open(file_path, 'r') + result = parser.syntax().parseFile(fh) + self.parse_tree(result) + fh.close() + except ParseException, err: + print err.line + print " "*(err.column-1) + "^" + print err + + def write_to_fs(self, path): + """Writes converted data into specified directory + """ + if os.path.exists(path): + shutil.rmtree(path) + + os.makedirs(path) + + with codecs.open(os.path.join(path, "data.scs"), "w", "utf-8") as output: + for t in Converter.triples: + if isinstance(t, str) or isinstance(t, unicode): + output.write(t + '\n') + else: + output.write("%s | %s | %s;;\n" % t) + output.close() + + # write contents + contents_dir = os.path.join(path, "contents") + os.makedirs(contents_dir) + for num, data in Converter.contents.items(): + f = open(os.path.join(contents_dir, str(num)), "w") + f.write(data) + f.close() + +if __name__ == "__main__": + + if len(sys.argv) < 3: + print "Usage: python converter.py " + sys.exit(0) + + converter = Converter() + converter.parse_directory(sys.argv[1]) + print "Write output..." + converter.write_to_fs(sys.argv[2]) diff --git a/tools/scs-converter/parser.py b/tools/scs-converter/parser.py new file mode 100644 index 00000000..6ac3245a --- /dev/null +++ b/tools/scs-converter/parser.py @@ -0,0 +1,436 @@ +import re, sys, traceback +from pyparsing import Word, Literal, Forward, Regex, Group, ZeroOrMore, SkipTo, ParserElement +from pyparsing import OneOrMore, srange, Keyword, QuotedString, ParseResults, Optional, cStyleComment + +ParserElement.enablePackrat() + +reKeyword = r'/!\*\s*keyword:\s*([a-zA-Z0-9_]+)\s*\*/' + +# parse results cache +maxCachedResults = 10 +cacheMap = {} +cacheQueue = [] + +connectors = [u'<>', + u'>', + u'<', + u'..>', + u'<..', + u'->', + u'<-', + u'<=>', + u'=>', + u'<=', + u'-|>', + u'<|-', + u'-/>', + u'', + u'<~', + u'~|>', + u'<|~', + u'~/>', + u' 1: + self.internal = tokens[0][1] + + def __call__(self, tokens): + return IdentifierGroup(tokens) + + def __str__(self): + if self.internal is not None: + return str(self.identifier) + str(self.internal) + + return str(self.identifier) + + def __eq__(self, other): + if not isinstance(other, IdentifierGroup): + return False + + if self.identifier != other.identifier: + return False + + return self.internal == other.internal + +class SimpleIdentifierGroup(BaseGroup): + """Class that represents simple identifier + """ + def __init__(self, tokens): + self.value = tokens[0] + + def __call__(self, tokens): + return SimpleIdentifierGroup(tokens) + + def __str__(self): + return str(self.value) + + def __eq__(self, other): + if not isinstance(other, SimpleIdentifierGroup): + return False + + return self.value == other.value + +class UrlGroup(BaseGroup): + + def __init__(self, tokens): + self.value = tokens[0] + + def __call__(self, tokens): + return UrlGroup(tokens) + + def __str__(self): + return str(self.value) + + def __eq__(self, other): + if not isinstance(other, SimpleIdentifierGroup): + return False + + return self.value == other.value + +class AliasGroup(BaseGroup): + """Class that represent identifiers, that hasn't name (names doesn't translate into memory) + """ + def __init__(self, tokens): + pass + + def __call__(self, tokens): + return AliasGroup(tokens) + + def __str__(self): + return 'ooo' + + def __eq__(self, other): + return False # they always doesn't equivalent + +class ContentGroup(BaseGroup): + """Class that represent content information + """ + def __init__(self, tokens): + self.value = tokens[0] + + def __call__(self, tokens): + return ContentGroup(tokens) + + def __str__(self): + return str(self.value) + + def __eq__(self, other): + if not isinstance(other, ContentGroup): + return False + + return self.value == other.value + +class SetGroup(BaseGroup): + """Class that represents set identifier + """ + def __init__(self, tokens): + self.items = tokens[0] + self.par = ('{', '}') + + def __call__(self, tokens): + return SetGroup(tokens) + + def __str__(self): + + res = self.par[0] + first = True + # TODO: sort items by alphabet + for item in self.items: + if not first: + res += ', ' + else: + first = False + res += str(item) + + res += self.par[1] + return res + + def __eq__(self, other): + # TODO: implement comparsion + return False + + +class OSetGroup(SetGroup): + + def __init__(self, tokens): + SetGroup.__init__(self, tokens) + self.par = ('<', '>') + + def __call__(self, tokens): + return OSetGroup(tokens) + + def __eq__(self, other): + + if len(self.items) != len(other.items): + return False + + for idx in xrange(len(self.items)): + if self.items[idx] != other.items[idx]: + return False + + return True + + def __str__(self): + + res = self.par[0] + first = True + # TODO: sort items by alphabet + for item in self.items: + if not first: + res += ', ' + else: + first = False + res += str(item) + + res += self.par[1] + return res + +class TripleGroup(BaseGroup): + """Class that represent triple + """ + def __init__(self, tokens): + self.subject = tokens[0][0] + self.predicate = tokens[0][1] + self.object = tokens[0][2] + + def __call__(self, tokens): + return TripleGroup(tokens) + + def __str__(self): + return "( " + str(self.subject) + " | " + str(self.predicate) + " | " + str(self.object) + " )" + + def __eq__(self, other): + return self.subject == other.subkect and self.predicate == other.predicate and self.object == other.object + + +class SimpleSentenceGroup(TripleGroup): + """Class that represents sentence of scs-code level 1 + """ + def __str__(self): + return TripleGroup.__str__(self)[2 : -2] + + def __eq__(self, other): + # TODO: implement comparsion + return False + +class SynonymGroup(BaseGroup): + + def __init__(self, tokens): + self.first = tokens[0][0] + self.second = tokens[0][1] + + def __call__(self, tokens): + return SynonymGroup(tokens) + + def __str__(self): + return "%s = %s" % (str(self.first), str(self.second)) + + def __eq__(self, other): + # TODO: implement comparsion + return False + +class SentenceGroup(BaseGroup): + """Class that represents sentence + """ + def __init__(self, tokens): + self.subject = tokens[0][0] + self.predicate = tokens[0][1] + self.attrs = tokens[0][2] + self.object = tokens[0][3] + + def __call__(self, tokens): + return SentenceGroup(tokens) + + def __str__(self): + return "sentence: " + str(self.subject) + " " + str(self.predicate) + " " + str(self.attrs) + " " + str(self.object) + + def __eq__(self, other): + # TODO: implement comparsion + return False + +class IdtfWithIntGroup(BaseGroup): + + def __init__(self, tokens): + self.idtf = tokens[0][0] + self.internal = None + if len(tokens[0]) > 1: + self.internal = tokens[0][1] + + def __call__(self, tokens): + return IdtfWithIntGroup(tokens) + + def __str__(self): + return "%s" % str(self.idtf) + + def __eq__(self, other): + # TODO: implement comparsion + return False + +class InternalGroup(BaseGroup): + + def __init__(self, tokens): + self.predicate = tokens[0][0] + self.attrs = tokens[0][1] + self.object = tokens[0][2] + + def __call__(self, tokens): + return InternalGroup(tokens) + + def __str__(self): + return '(* ' + str(self.predicate) + str(self.attrs) + str(self.object) + ' *)' + +class InternalListGroup(BaseGroup): + + def __init__(self, tokens): + self.sentences = tokens[0] + + def __call__(self, tokens): + return InternalListGroup(tokens) + + def __str__(self): + return str(self.sentences) + +def syntax(): + + syntax = None + + name = Word(srange(u"[a-zA-Z0-9_#]"), srange(u"[a-zA-Z0-9_#]")).setParseAction(SimpleIdentifierGroup) + url = QuotedString(quoteChar='"', unquoteResults=False).setParseAction(UrlGroup) + simpleIdtf = name ^ url + + tripleSep = Literal(u'|').suppress() + attrSep = Literal(u':').suppress() + objSep = Literal(u';').suppress() + sentSep = Literal(u';;').suppress() + synSep = Literal(u'=').suppress() + lpar = Literal(u'(').suppress() + rpar = Literal(u')').suppress() + aliasNoName = Literal(u'***').suppress() + lpar_set = Literal(u'{').suppress() + rpar_set = Literal(u'}').suppress() + lpar_oset = Literal(u'<').suppress() + rpar_oset = Literal(u'>').suppress() + lpar_trf = Literal(u'[').suppress() + rpar_trf = Literal(u']').suppress() + lpar_int = Literal(u'(*').suppress() + rpar_int = Literal(u'*)').suppress() + + # comments + comment_keyword = Regex(reKeyword).setParseAction(KeywordGroup) + comment = comment_keyword + + # level 1 sentence + sentence_lv1 = Group(simpleIdtf + tripleSep + simpleIdtf + tripleSep + simpleIdtf).setParseAction(SimpleSentenceGroup) + + + # other levels sentence + connector = None + for c in connectors: + if c == connectors[0]: + connector = Literal(c) ^ Literal(u'_' + c) + else: + connector = connector ^ Literal(c) ^ Literal(u'_' + c) + + # identifiers + idtf = Forward() + internal = Forward() + + attrsList = Group(ZeroOrMore(simpleIdtf + attrSep)) + + # internal sentence + idtfWithInt = Group(idtf + Optional(internal)).setParseAction(IdtfWithIntGroup) + objectList = Group(idtfWithInt + ZeroOrMore(objSep + idtfWithInt)) + intSentence = Group(connector + Optional(attrsList) + objectList).setParseAction(InternalGroup) + + intSentenceList = Group(lpar_int + OneOrMore(intSentence + sentSep) + rpar_int).setParseAction(InternalListGroup) + + internal << intSentenceList + + content = QuotedString(quoteChar=u'[', endQuoteChar=u']', unquoteResults=True, multiline=True, escChar=u'\\').setParseAction(ContentGroup) + triple = Group(lpar + idtf + connector + idtf + rpar).setParseAction(TripleGroup) + alias = Group(aliasNoName).setParseAction(AliasGroup) + + setIdtf = Group(lpar_set + ZeroOrMore(Group(Optional(attrsList) + idtfWithInt) + objSep) + Group(Optional(attrsList) + idtfWithInt) + rpar_set).setParseAction(SetGroup) + osetIdtf = Group(lpar_oset + ZeroOrMore(Group(Optional(attrsList) + idtfWithInt) + objSep) + Group(Optional(attrsList) + idtfWithInt) + rpar_oset).setParseAction(OSetGroup) + + anyIdtf = simpleIdtf ^ content ^ triple ^ setIdtf ^ osetIdtf ^ alias + idtf << anyIdtf + +# sentence_synonym = Group(idtf + synSep + idtf).setParseAction(SynonymGroup) + sentence_lv23456 = Group(idtf + connector + Optional(attrsList) + objectList).setParseAction(SentenceGroup) + + sentence = (sentence_lv1 ^ sentence_lv23456)# ^ sentence_synonym) + syntax = ZeroOrMore(Group(sentence + sentSep) ^ comment) + + syntax.ignore(cStyleComment) + + #syntax.setDebug() + + return syntax + +def parse(path): + """Parse scs file with specified \p path + """ + data = None + try: + f = open(path, 'r') + data = f.read().decode("utf-8") + f.close() + + fields = syntax().parseString(data) + + except: + print 'Error to parse "%s" file"' % path + print "Error:", sys.exc_info()[0] + traceback.print_exc(file=sys.stdout) + return None + + return fields + + diff --git a/tools/scs-converter/tests/test.scs b/tools/scs-converter/tests/test.scs new file mode 100644 index 00000000..6fa3fc43 --- /dev/null +++ b/tools/scs-converter/tests/test.scs @@ -0,0 +1,13 @@ +1 | "test" | 667;; +5 -> 6: 7: tr; ty;; +6 <- 8: re; gg; hj;; + +file -> "file://image.png";; +set -> attr_set: { sum: 1; result: 2; 3};; +set ~> < attr1: 1; attr2: 2; attr3: 3>;; + +el1 _<=> el2;; + +file => nrel_system_identifier: ["file"] (* <- string; russian;; <~ opened;;*);; + + From 4d146736f38547c464fd55e12bac3d523e127289 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Fri, 23 Nov 2012 01:06:32 +0300 Subject: [PATCH 04/84] added license --- tools/scs-converter/converter.py | 23 +++++++++++++++++++++++ tools/scs-converter/parser.py | 22 ++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/tools/scs-converter/converter.py b/tools/scs-converter/converter.py index ab643f7e..9012dfc4 100644 --- a/tools/scs-converter/converter.py +++ b/tools/scs-converter/converter.py @@ -1,4 +1,27 @@ # -*- coding: utf-8 -*- + +""" +----------------------------------------------------------------------------- +This source file is part of OSTIS (Open Semantic Technology for Intelligent Systems) +For the latest info, see http://www.ostis.net + +Copyright (c) 2012 OSTIS + +OSTIS is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OSTIS 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with OSTIS. If not, see . +----------------------------------------------------------------------------- +""" + import os, sys, shutil import codecs import re, sys, traceback diff --git a/tools/scs-converter/parser.py b/tools/scs-converter/parser.py index 6ac3245a..e8d96a1f 100644 --- a/tools/scs-converter/parser.py +++ b/tools/scs-converter/parser.py @@ -1,3 +1,25 @@ +""" +----------------------------------------------------------------------------- +This source file is part of OSTIS (Open Semantic Technology for Intelligent Systems) +For the latest info, see http://www.ostis.net + +Copyright (c) 2012 OSTIS + +OSTIS is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OSTIS 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with OSTIS. If not, see . +----------------------------------------------------------------------------- +""" + import re, sys, traceback from pyparsing import Word, Literal, Forward, Regex, Group, ZeroOrMore, SkipTo, ParserElement from pyparsing import OneOrMore, srange, Keyword, QuotedString, ParseResults, Optional, cStyleComment From a0446815769c7b81722353fed3b283b2a1b245f7 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Sun, 25 Nov 2012 23:19:11 +0300 Subject: [PATCH 05/84] work on scs-converter --- tools/scs-converter/converter.py | 142 +++++++++++++++++++++---------- tools/scs-converter/parser.py | 26 +++--- 2 files changed, 110 insertions(+), 58 deletions(-) diff --git a/tools/scs-converter/converter.py b/tools/scs-converter/converter.py index 9012dfc4..626bdf09 100644 --- a/tools/scs-converter/converter.py +++ b/tools/scs-converter/converter.py @@ -99,8 +99,8 @@ u'_<=>': "sc_edge_var", u'=>': "sc_arc_common_const", u'<=': "sc_arc_common_const", - u'=>': "sc_arc_common_var", - u'<=': "sc_arc_common_var", + u'_=>': "sc_arc_common_var", + u'_<=': "sc_arc_common_var", u'_->': "sc_arc_access_var_pos_perm", u'_<-': "sc_arc_access_var_pos_perm", u'-|>': "sc_arc_access_const_neg_perm", @@ -125,18 +125,9 @@ u'_', False), res, False) @@ -233,7 +244,7 @@ def processUrlGroup(self, group): pass def processKeywordGroup(self, group): - pass + return group def processSimpleSentenceGroup(self, group): """Process scs-level 1 sentences @@ -264,13 +275,13 @@ def processIdtfWithIntGroup(self, group): def processInternalGroup(self, group): - pass + return group def processInternalListGroup(self, group): - pass + return group def processTripleGroup(self, group): - pass + return group def processAliasGroup(self, group): """Just resolve identifier for alias @@ -278,7 +289,39 @@ def processAliasGroup(self, group): return self.resolve_identifier(group) def processContentGroup(self, group): - pass + """Store link content for saving + """ + if len(group.value) > 1 and group.value[0] == u'*' and group.value[-1] == u'*': + data = group.value[1:-1] + + # create new converter and build data + converter = Converter() + converter.link_contents = self.link_contents + converter.synonyms = self.synonyms + converter.triples = self.triples + converter.aliases = self.aliases + converter.set_count = self.set_count + converter.oset_count = self.oset_count + converter.arc_count = self.arc_count + converter.link_count = self.link_count + + converter.parse_string(data) + + self.link_contents = converter.link_contents + self.synonyms = converter.synonyms + self.triples = converter.triples + self.aliases = converter.aliases + self.set_count = converter.set_count + self.oset_count = converter.oset_count + self.arc_count = converter.arc_count + self.link_count = converter.link_count + + + link_idtf = self.generate_link_idtf() + self.link_contents[link_idtf] = group.value + group.value = '"file://%s"' % link_idtf + + return link_idtf def processSetGroup(self, group): """Process set @@ -370,7 +413,18 @@ def parse_tree(self, tree): else: self.group_processors[tree.__class__](tree) - + + def parse_string(self, data): + """Parse specified string + """ + try: + result = parser.syntax().parseString(data.decode('utf-8'), parseAll = True) + self.parse_tree(result) + except ParseException, err: + print err.line + print " "*(err.column-1) + "^" + print err + def parse_directory(self, path): """Parse specified directory """ @@ -385,19 +439,13 @@ def parse_directory(self, path): file_path = os.path.join(root, f) - self.triples.append('/* ------' + file_path + ' ----- */') + self.comments[len(self.triples)] = file_path # parse file - try: - print "Parse %s" % file_path - fh = open(file_path, 'r') - result = parser.syntax().parseFile(fh) - self.parse_tree(result) - fh.close() - except ParseException, err: - print err.line - print " "*(err.column-1) + "^" - print err + print "Parse %s" % file_path + fh = open(file_path, 'r') + self.parse_string(fh.read()) + fh.close() def write_to_fs(self, path): """Writes converted data into specified directory @@ -407,19 +455,21 @@ def write_to_fs(self, path): os.makedirs(path) + count = 0 with codecs.open(os.path.join(path, "data.scs"), "w", "utf-8") as output: - for t in Converter.triples: - if isinstance(t, str) or isinstance(t, unicode): - output.write(t + '\n') - else: - output.write("%s | %s | %s;;\n" % t) + for t in self.triples: + + if self.comments.has_key(count): + output.write('\n/* --- %s --- */\n' % self.comments[count]) + + output.write("%s | %s | %s;;\n" % t) + count += 1 output.close() # write contents - contents_dir = os.path.join(path, "contents") - os.makedirs(contents_dir) - for num, data in Converter.contents.items(): - f = open(os.path.join(contents_dir, str(num)), "w") + os.makedirs(os.path.join(path, "data")) + for num, data in self.link_contents.items(): + f = open(os.path.join(path, str(num)), "w") f.write(data) f.close() diff --git a/tools/scs-converter/parser.py b/tools/scs-converter/parser.py index e8d96a1f..6427b405 100644 --- a/tools/scs-converter/parser.py +++ b/tools/scs-converter/parser.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + """ ----------------------------------------------------------------------------- This source file is part of OSTIS (Open Semantic Technology for Intelligent Systems) @@ -362,8 +364,8 @@ def syntax(): syntax = None - name = Word(srange(u"[a-zA-Z0-9_#]"), srange(u"[a-zA-Z0-9_#]")).setParseAction(SimpleIdentifierGroup) - url = QuotedString(quoteChar='"', unquoteResults=False).setParseAction(UrlGroup) + name = Word(srange(u"[a-zA-Z0-9_#]"), srange(u"[a-zA-Z0-9_#]")).setParseAction(SimpleIdentifierGroup).setName("Name") + url = QuotedString(quoteChar='"', unquoteResults=False).setParseAction(UrlGroup).setName("Url") simpleIdtf = name ^ url tripleSep = Literal(u'|').suppress() @@ -388,7 +390,7 @@ def syntax(): comment = comment_keyword # level 1 sentence - sentence_lv1 = Group(simpleIdtf + tripleSep + simpleIdtf + tripleSep + simpleIdtf).setParseAction(SimpleSentenceGroup) + sentence_lv1 = Group(simpleIdtf + tripleSep + simpleIdtf + tripleSep + simpleIdtf).setParseAction(SimpleSentenceGroup).setName("SimpleSentence") # other levels sentence @@ -406,26 +408,26 @@ def syntax(): attrsList = Group(ZeroOrMore(simpleIdtf + attrSep)) # internal sentence - idtfWithInt = Group(idtf + Optional(internal)).setParseAction(IdtfWithIntGroup) + idtfWithInt = Group(idtf + Optional(internal)).setParseAction(IdtfWithIntGroup).setName("IdtfWithIntGroup") objectList = Group(idtfWithInt + ZeroOrMore(objSep + idtfWithInt)) - intSentence = Group(connector + Optional(attrsList) + objectList).setParseAction(InternalGroup) + intSentence = Group(connector + Optional(attrsList) + objectList).setParseAction(InternalGroup).setName("InternalSentence") - intSentenceList = Group(lpar_int + OneOrMore(intSentence + sentSep) + rpar_int).setParseAction(InternalListGroup) + intSentenceList = Group(lpar_int + OneOrMore(intSentence + sentSep) + rpar_int).setParseAction(InternalListGroup).setName("InternalSentenceGroup") internal << intSentenceList - content = QuotedString(quoteChar=u'[', endQuoteChar=u']', unquoteResults=True, multiline=True, escChar=u'\\').setParseAction(ContentGroup) - triple = Group(lpar + idtf + connector + idtf + rpar).setParseAction(TripleGroup) - alias = Group(aliasNoName).setParseAction(AliasGroup) + content = QuotedString(quoteChar=u'[', endQuoteChar=u']', unquoteResults=True, multiline=True, escChar=u'\\').setParseAction(ContentGroup).setName("Content") + triple = Group(lpar + idtf + connector + idtf + rpar).setParseAction(TripleGroup).setName("Triple") + alias = Group(aliasNoName).setParseAction(AliasGroup).setName("Alias") - setIdtf = Group(lpar_set + ZeroOrMore(Group(Optional(attrsList) + idtfWithInt) + objSep) + Group(Optional(attrsList) + idtfWithInt) + rpar_set).setParseAction(SetGroup) - osetIdtf = Group(lpar_oset + ZeroOrMore(Group(Optional(attrsList) + idtfWithInt) + objSep) + Group(Optional(attrsList) + idtfWithInt) + rpar_oset).setParseAction(OSetGroup) + setIdtf = Group(lpar_set + ZeroOrMore(Group(Optional(attrsList) + idtfWithInt) + objSep) + Group(Optional(attrsList) + idtfWithInt) + rpar_set).setParseAction(SetGroup).setName("Set") + osetIdtf = Group(lpar_oset + ZeroOrMore(Group(Optional(attrsList) + idtfWithInt) + objSep) + Group(Optional(attrsList) + idtfWithInt) + rpar_oset).setParseAction(OSetGroup).setName("OSet") anyIdtf = simpleIdtf ^ content ^ triple ^ setIdtf ^ osetIdtf ^ alias idtf << anyIdtf # sentence_synonym = Group(idtf + synSep + idtf).setParseAction(SynonymGroup) - sentence_lv23456 = Group(idtf + connector + Optional(attrsList) + objectList).setParseAction(SentenceGroup) + sentence_lv23456 = Group(idtf + connector + Optional(attrsList) + objectList).setParseAction(SentenceGroup).setName("Sentence") sentence = (sentence_lv1 ^ sentence_lv23456)# ^ sentence_synonym) syntax = ZeroOrMore(Group(sentence + sentSep) ^ comment) From 597367426cc2ef8be99a5e4c1c37c52df85ab3db Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Sun, 25 Nov 2012 23:47:01 +0300 Subject: [PATCH 06/84] fixed some errors with internal sentences for sc-link --- tools/scs-converter/converter.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/tools/scs-converter/converter.py b/tools/scs-converter/converter.py index 626bdf09..15b229a8 100644 --- a/tools/scs-converter/converter.py +++ b/tools/scs-converter/converter.py @@ -191,16 +191,6 @@ def generate_arc_idtf(self, type = None, include_into_set = True): return res - def process_arc_connector(self, connector): - assert connector != "=" - global arc_id - - # todo fixme - res = "sc_arc_common#%d" % arc_id - arc_id += 1 - - return res - def resolve_identifier(self, group): """Resolves identifiers for different groups """ @@ -254,14 +244,15 @@ def processSimpleSentenceGroup(self, group): def processIdtfWithIntGroup(self, group): """Process identifier with internal sentence group """ - subject_idtf = self.resolve_identifier(group.idtf) self.parse_tree(group.idtf) + subject_idtf = self.resolve_identifier(group.idtf) internal_list = group.internal if internal_list is not None: for sentence in internal_list.sentences: for obj in sentence.object: + self.parse_tree(obj) object_idtf = self.resolve_identifier(obj) attributes = sentence.attrs arc_idtf = self.generate_arc_idtf(sentence.predicate) @@ -315,6 +306,8 @@ def processContentGroup(self, group): self.oset_count = converter.oset_count self.arc_count = converter.arc_count self.link_count = converter.link_count + + # todo add arcs into contour elements link_idtf = self.generate_link_idtf() From e5d78d3d8eabb97f282ca83285d09ebcab953f5f Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Thu, 29 Nov 2012 02:15:30 +0300 Subject: [PATCH 07/84] added python tool for repository building; fixed errors in converter --- tools/scs-converter/builder.py | 386 +++++++++++++++++++++++++++++ tools/scs-converter/converter.py | 164 ++++++------ tools/scs-converter/tests/test.scs | 2 +- 3 files changed, 478 insertions(+), 74 deletions(-) create mode 100644 tools/scs-converter/builder.py diff --git a/tools/scs-converter/builder.py b/tools/scs-converter/builder.py new file mode 100644 index 00000000..2a6083bb --- /dev/null +++ b/tools/scs-converter/builder.py @@ -0,0 +1,386 @@ +# -*- coding: utf-8 -*- +""" +----------------------------------------------------------------------------- +This source file is part of OSTIS (Open Semantic Technology for Intelligent Systems) +For the latest info, see http://www.ostis.net + +Copyright (c) 2012 OSTIS + +OSTIS is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +OSTIS 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with OSTIS. If not, see . +----------------------------------------------------------------------------- +""" + +import os, sys, shutil +import codecs +import converter +from sc_memory import * + +encoding = "utf-8" +reload(sys) +sys.setdefaultencoding(encoding) +sys.stdout = codecs.getwriter(encoding)(sys.stdout, errors = "replace") +sys.stderr = codecs.getwriter(encoding)(sys.stderr, errors = "replace") + +# mapping of identifier prefix into sc-type +arcPrefixToType = { + u"sc_arc_common" : sc_type_arc_common, + u"sc_arc_main": sc_type_arc_pos_const_perm, + u"sc_edge" : sc_type_edge_common, + u"sc_arc_access": sc_type_arc_access, + } + +# mapping of set identifiers into sc-types +arcIdtfToType = { + u"sc_edge_const": sc_type_edge_common | sc_type_const, + u"sc_edge_var": sc_type_edge_common | sc_type_var, + u"sc_arc_common_const": sc_type_arc_common | sc_type_const, + u"sc_arc_common_var": sc_type_arc_common | sc_type_var, + + u"sc_arc_access_var_pos_perm": sc_type_arc_access | sc_type_var | sc_type_arc_pos | sc_type_arc_perm, + u"sc_arc_access_const_neg_perm": sc_type_arc_access | sc_type_const | sc_type_arc_neg | sc_type_arc_perm, + u"sc_arc_access_var_neg_perm": sc_type_arc_access | sc_type_var | sc_type_arc_neg | sc_type_arc_perm, + u"sc_arc_access_const_fuz_perm": sc_type_arc_access | sc_type_const | sc_type_arc_fuz | sc_type_arc_perm, + u"sc_arc_access_var_fuz_perm": sc_type_arc_access | sc_type_var | sc_type_arc_fuz | sc_type_arc_perm, + + u"sc_arc_access_const_pos_temp": sc_type_arc_access | sc_type_const | sc_type_arc_pos | sc_type_arc_temp, + u"sc_arc_access_var_pos_temp": sc_type_arc_access | sc_type_var | sc_type_arc_pos | sc_type_arc_temp, + u"sc_arc_access_const_neg_temp": sc_type_arc_access | sc_type_const | sc_type_arc_neg | sc_type_arc_temp, + u"sc_arc_access_var_neg_temp": sc_type_arc_access | sc_type_var | sc_type_arc_neg | sc_type_arc_temp, + u"sc_arc_access_const_fuz_temp": sc_type_arc_access | sc_type_const | sc_type_arc_fuz | sc_type_arc_temp, + u"sc_arc_access_var_fuz_temp": sc_type_arc_access | sc_type_var | sc_type_arc_fuz | sc_type_arc_temp, + } + +# mapping identifiers into sc-addrs +sc_addrs = {} +# map of sc-element types +sc_types = {} +# map of sc-arc, that need to be created +sc_arcs = {} + +# statistics +created_nodes = 0 +created_links = 0 +created_arcs = 0 + +nrel_idtf_addr = None + +input_path = None +output_path = None + +# -------------------------------------------------- + +class ScElement: + def __init__(self): + self.type = sc_type_node + self.idtf = 0 + +class ScNode(ScElement): + def __init__(self): + ScElement.__init__(self) + +class ScArc(ScElement): + def __init__(self): + ScElement.__init__(self) + self.sc_addr = None + self.begin = None + self.end = None + +class ScLink(ScElement): + def __init__(self): + ScElement.__init__(self) + +# -------------------------------------------------- + +def checkIdtfWithPrefix(idtf): + """Check if specified identifier contains type + """ + return idtf.count(u'#') > 0 + +def checkIdtfIsLink(idtf): + """Checks if specified idtf is and sc-link + """ + return len(idtf) > 1 and idtf[0] == u'"' and idtf[-1] == u'"' + +def checkIdtfIsArc(idtf): + """Check if specified identifier is an arc identifier. + This function analyze just identifier + """ + if checkIdtfWithPrefix(idtf): + preffix, el_idtf = splitIdtf(idtf) + if arcPrefixToType.has_key(preffix): + return True + + return False + +def splitIdtf(idtf): + """Split identifier with prefix to type and identifier + """ + assert checkIdtfWithPrefix(idtf) + + res = idtf.split('#') + return (res[0], res[1]) + + +def determineArcType(arc_idtf): + """Determine type of predicate + """ + if checkIdtfWithPrefix(arc_idtf): + prefix, idtf = splitIdtf(arc_idtf) + return arcPrefixToType[prefix] + + return sc_type_arc_common + +def determineElementType(idtf, isPredicate): + """Determines element type and store it in types map + """ + oldType = None + try: + oldType = sc_types[idtf] + except: + pass + + newType = None + if checkIdtfIsLink(idtf): + newType = sc_type_link + else: + if checkIdtfIsArc(idtf): + newType = determineArcType(idtf) + + if newType is None: + if isPredicate: + newType = sc_type_arc_common + else: + newType = sc_type_node + + if oldType is not None: + # determine if new element type more common then old type + if newType == (newType & oldType): + sc_types[idtf] = newType + else: + sc_types[idtf] = newType + +def resolveLinkPath(idtf): + """Resolve sc-link file path + """ + path = idtf[1:-1] + path = path.replace("file://", "") + + return os.path.join(input_path, path) + + +def createNodeOrLink(elIdtf, elType): + """Create sc-node or sc-link in memory + """ + if elType & sc_type_link: + addr = sc_memory_link_new() + global created_links + created_links += 1 + + # setup link data + path = elIdtf[1:-1] + path = path.replace("file://", "") + if conv.link_contents.has_key(path): + data = str(conv.link_contents[path]) + stream = sc_stream_memory_new(data, len(data), SC_STREAM_READ, False) + else: + path = resolveLinkPath(elIdtf) + stream = sc_stream_file_new(path, SC_STREAM_READ) + + if stream is None: + print "Can't setup content from path %s" % path + else: + sc_memory_set_link_content(addr, stream) + sc_stream_free(stream) + + elif elType & sc_type_node: + + addr = sc_memory_node_new(elType) + global created_nodes + created_nodes += 1 + + else: + raise "Unknown type" + + sc_addrs[elIdtf] = addr + +def resolveScAddr(idtf): + """Resolve sc-addr of element if it possible + """ + + if sc_addrs.has_key(idtf): + return + + obj_type = sc_types[idtf] + # create subject and object if them aren't an arcs + if not (obj_type & sc_type_arc_mask): + createNodeOrLink(idtf, obj_type) + +def generateSystemIdentifier(el_addr, idtf): + """Generate system identifier construction for specified node + """ + global created_nodes + global created_arcs + global created_links + + idtf_data = str(idtf) + stream = sc_stream_memory_new(idtf_data, len(idtf_data), SC_STREAM_READ, False) + assert stream is not None + idtf_link = sc_memory_link_new() + sc_memory_set_link_content(idtf_link, stream) + sc_stream_free(stream) + + # link nodes + arc_addr = sc_memory_arc_new(sc_type_arc_common | sc_type_const, el_addr, idtf_link) + sc_memory_arc_new(sc_type_arc_pos_const_perm, nrel_idtf_addr, arc_addr) + + created_links += 1 + created_arcs += 2 + +def generateIdentifiers(): + + global nrel_idtf_addr + + # create 'nrel_system_identifier' keynode if it doesn't exist + nrel_idtf_str = u'nrel_system_identifier' + nrel_idtf_data = str(nrel_idtf_str) + + + try: + nrel_idtf_addr = sc_addrs[nrel_idtf_str] + except: + raise "You need to define system identifier keynode in scs" + + assert nrel_idtf_addr is not None + + for idtf, addr in sc_addrs.items(): + + # extract object identifier + system_idtf = idtf + if checkIdtfWithPrefix(system_idtf): + system_idtf = splitIdtf(system_idtf)[1] + + if system_idtf.startswith('.') or (system_idtf.startswith('"') and system_idtf.endswith('"')): + continue + + print "\tSetup for %s" % idtf + + assert addr is not None + # generate identifier relation + generateSystemIdentifier(addr, system_idtf) + + +# ------------------------------------------------ + +if __name__ == "__main__": + + print "Default encoding: %s" % sys.getdefaultencoding() + + if len(sys.argv) < 3: + print "Usage: python builder.py " + sys.exit(0) + + global input_path + global output_path + + input_path = sys.argv[1] + output_path = sys.argv[2] + + sc_memory_initialize(output_path) + + conv = converter.Converter() + conv.parse_directory(input_path) + + # determine types of all objects + print "Determine list of all objects and their types..." + for triple in conv.triples: + for idx in xrange(3): + determineElementType(triple[idx], idx == 1) + + print "\tFound %d elements" % len(sc_types) + + # process triples + print "Resolve sc-addrs..." + for triple in conv.triples: + subject = triple[0] + object = triple[2] + predicate = triple[1] + + resolveScAddr(subject) + resolveScAddr(object) + + # store arc + arc = ScArc() + arc.begin = subject + arc.end = object + arc.type = sc_types[predicate] + + sc_arcs[predicate] = arc + + # now create arcs, while there are any arcs not created, + # or any of them couldn't be created anymore + print "Create arcs..." + created = True + while len(sc_arcs) > 0 and created: + created = False + + created_list = [] + + # iterate all arcs that wasn't created and try to create them + for item in sc_arcs.iteritems(): + idtf, arc = item + + # determine if begin and end arc elements created + begin_addr = None + end_addr = None + + try: + begin_addr = sc_addrs[arc.begin] + end_addr = sc_addrs[arc.end] + except: + continue + + # create new arc + addr = sc_memory_arc_new(arc.type, begin_addr, end_addr) + sc_addrs[idtf] = addr + created_list.append(idtf) + + global created_arcs + created_arcs += 1 + + created = (len(created_list) > 0) + # remove created arcs from map + for arc in created_list: + sc_arcs.pop(arc) + + + # generate system identifiers + print "Setup system identifiers..." + generateIdentifiers() + + sc_memory_shutdown() + + # write list of arcs, that wasn't created + for idtf in sc_arcs.iterkeys(): + print "Arc %s wasn't created" % idtf + + + + all_count = created_links + created_nodes + created_arcs + print "Statistics:" + print "\tCreated nodes: %d (%.03f%%)" % (created_nodes, float(created_nodes) / all_count * 100) + print "\tCreated links: %d (%.03f%%)" % (created_links, float(created_links) / all_count * 100) + print "\tCreated arcs: %d (%.03f%%)" % (created_arcs, float(created_arcs) / all_count * 100) + print "\tTotal: %d" % all_count + + print "" \ No newline at end of file diff --git a/tools/scs-converter/converter.py b/tools/scs-converter/converter.py index 15b229a8..c97d4d3a 100644 --- a/tools/scs-converter/converter.py +++ b/tools/scs-converter/converter.py @@ -32,6 +32,12 @@ ParserElement.enablePackrat() +encoding = "utf-8" +reload(sys) +sys.setdefaultencoding(encoding) +sys.stdout = codecs.getwriter(encoding)(sys.stdout, errors = "replace") +sys.stderr = codecs.getwriter(encoding)(sys.stderr, errors = "replace") + reKeyword = r'/!\*\s*keyword:\s*([a-zA-Z0-9_]+)\s*\*/' arc_id = 0 @@ -57,72 +63,72 @@ ] arc_types = { - u">" : "sc_arc_common", - u"<" : "sc_arc_common", - u"->": "sc_arc_main", - u"<-": "sc_arc_main", - u"<>": "sc_edge", - u"..>": "sc_arc_access", - u"<..": "sc_arc_access", - u'<=>': "sc_edge", - u'_<=>': "sc_edge", - u'=>': "sc_arc_common", - u'<=': "sc_arc_common", - u'=>': "sc_arc_common", - u'<=': "sc_arc_common", - u'_->': "sc_arc_access", - u'_<-': "sc_arc_access", - u'-|>': "sc_arc_access", - u'_-|>': "sc_arc_access", - u'<|-': "sc_arc_access", - u'_<|-': "sc_arc_access", - u'-/>': "sc_arc_access", - u'_-/>': "sc_arc_access", - u'': "sc_arc_access", - u'_~>': "sc_arc_access", - u'<~': "sc_arc_access", - u'_<~': "sc_arc_access", - u'~|>': "sc_arc_access", - u'_~|>': "sc_arc_access", - u'<|~': "sc_arc_access", - u'_<|~': "sc_arc_access", - u'~/>': "sc_arc_access", - u'_~/>': "sc_arc_access", - u'" : u"sc_arc_common", + u"<" : u"sc_arc_common", + u"->": u"sc_arc_main", + u"<-": u"sc_arc_main", + u"<>": u"sc_edge", + u"..>": u"sc_arc_access", + u"<..": u"sc_arc_access", + u'<=>': u"sc_edge", + u'_<=>': u"sc_edge", + u'=>': u"sc_arc_common", + u'<=': u"sc_arc_common", + u'=>': u"sc_arc_common", + u'<=': u"sc_arc_common", + u'_->': u"sc_arc_access", + u'_<-': u"sc_arc_access", + u'-|>': u"sc_arc_access", + u'_-|>': u"sc_arc_access", + u'<|-': u"sc_arc_access", + u'_<|-': u"sc_arc_access", + u'-/>': u"sc_arc_access", + u'_-/>': u"sc_arc_access", + u'': u"sc_arc_access", + u'_~>': u"sc_arc_access", + u'<~': u"sc_arc_access", + u'_<~': u"sc_arc_access", + u'~|>': u"sc_arc_access", + u'_~|>': u"sc_arc_access", + u'<|~': u"sc_arc_access", + u'_<|~': u"sc_arc_access", + u'~/>': u"sc_arc_access", + u'_~/>': u"sc_arc_access", + u'': "sc_edge_const", - u'_<=>': "sc_edge_var", - u'=>': "sc_arc_common_const", - u'<=': "sc_arc_common_const", - u'_=>': "sc_arc_common_var", - u'_<=': "sc_arc_common_var", - u'_->': "sc_arc_access_var_pos_perm", - u'_<-': "sc_arc_access_var_pos_perm", - u'-|>': "sc_arc_access_const_neg_perm", - u'_-|>': "sc_arc_access_var_neg_perm", - u'<|-': "sc_arc_access_const_neg_perm", - u'_<|-': "sc_arc_access_var_neg_perm", - u'-/>': "sc_arc_access_const_fuz_perm", - u'_-/>': "sc_arc_access_var_fuz_perm", - u'': "sc_arc_access_const_pos_temp", - u'_~>': "sc_arc_access_var_pos_temp", - u'<~': "sc_arc_access_const_pos_temp", - u'_<~': "sc_arc_access_var_pos_temp", - u'~|>': "sc_arc_access_const_neg_temp", - u'_~|>': "sc_arc_access_var_neg_temp", - u'<|~': "sc_arc_access_const_neg_temp", - u'_<|~': "sc_arc_access_var_neg_temp", - u'~/>': "sc_arc_access_const_fuz_temp", - u'_~/>': "sc_arc_access_var_fuz_temp", - u'': u"sc_edge_const", + u'_<=>': u"sc_edge_var", + u'=>': u"sc_arc_common_const", + u'<=': u"sc_arc_common_const", + u'_=>': u"sc_arc_common_var", + u'_<=': u"sc_arc_common_var", + u'_->': u"sc_arc_access_var_pos_perm", + u'_<-': u"sc_arc_access_var_pos_perm", + u'-|>': u"sc_arc_access_const_neg_perm", + u'_-|>': u"sc_arc_access_var_neg_perm", + u'<|-': u"sc_arc_access_const_neg_perm", + u'_<|-': u"sc_arc_access_var_neg_perm", + u'-/>': u"sc_arc_access_const_fuz_perm", + u'_-/>': u"sc_arc_access_var_fuz_perm", + u'': u"sc_arc_access_const_pos_temp", + u'_~>': u"sc_arc_access_var_pos_temp", + u'<~': u"sc_arc_access_const_pos_temp", + u'_<~': u"sc_arc_access_var_pos_temp", + u'~|>': u"sc_arc_access_const_neg_temp", + u'_~|>': u"sc_arc_access_var_neg_temp", + u'<|~': u"sc_arc_access_const_neg_temp", + u'_<|~': u"sc_arc_access_var_neg_temp", + u'~/>': u"sc_arc_access_const_fuz_temp", + u'_~/>': u"sc_arc_access_var_fuz_temp", + u'', False), res, False) @@ -221,6 +227,10 @@ def check_predicate_mirror(self, predicate): def append_sentence(self, subject, predicate, object, isMirrored): """Appends new scs-level 1 sentence into list """ + assert isinstance(subject, str) or isinstance(subject, unicode) + assert isinstance(object, str) or isinstance(object, unicode) + assert isinstance(predicate, str) or isinstance(predicate, unicode) + if not isMirrored: self.triples.append((subject, predicate, object)) else: @@ -228,7 +238,7 @@ def append_sentence(self, subject, predicate, object, isMirrored): # --------------------------------------- def processSimpleIdentifierGroup(self, group): - return self.resolve_identifier(group) + self.resolve_identifier(group) def processUrlGroup(self, group): pass @@ -239,7 +249,11 @@ def processKeywordGroup(self, group): def processSimpleSentenceGroup(self, group): """Process scs-level 1 sentences """ - self.append_sentence(group.subject, group.predicate, group.object, False); + subject_idtf = self.resolve_identifier(group.subject) + object_idtf = self.resolve_identifier(group.object) + arc_idtf = group.predicate.value + + self.append_sentence(subject_idtf, arc_idtf, object_idtf, False); def processIdtfWithIntGroup(self, group): """Process identifier with internal sentence group @@ -343,15 +357,16 @@ def processOSetGroup(self, group): object = item[1] item_count += 1 + object_idtf = self.resolve_identifier(object) self.parse_tree(object) arc_idtf = self.generate_arc_idtf('->') - self.append_sentence(idtf, arc_idtf, self.resolve_identifier(object), False) + self.append_sentence(idtf, arc_idtf, object_idtf, False) # add order attribute self.append_sentence("%d_" % item_count, self.generate_arc_idtf('->'), arc_idtf, False) for attr in attributes: attr_idtf = self.resolve_identifier(attr) - self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), object, False) + self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), object_idtf, False) def processSentenceGroup(self, group): """Process sentence for scs-levels 2-6 @@ -362,6 +377,7 @@ def processSentenceGroup(self, group): objects = group.object self.parse_tree(subject) + subject_idtf = self.resolve_identifier(subject) for obj in objects: # process object self.parse_tree(obj) @@ -370,7 +386,7 @@ def processSentenceGroup(self, group): # connect subject with object arc_idtf = self.generate_arc_idtf(predicate) - self.append_sentence(subject, arc_idtf, obj_idtf, self.check_predicate_mirror(predicate)) + self.append_sentence(subject_idtf, arc_idtf, obj_idtf, self.check_predicate_mirror(predicate)) # connect attributes for attr in attributes: @@ -436,9 +452,9 @@ def parse_directory(self, path): # parse file print "Parse %s" % file_path - fh = open(file_path, 'r') - self.parse_string(fh.read()) - fh.close() + input = open(file_path, "r") + self.parse_string(input.read().decode("utf-8")) + input.close() def write_to_fs(self, path): """Writes converted data into specified directory @@ -468,6 +484,8 @@ def write_to_fs(self, path): if __name__ == "__main__": + print "Default encoding: %s" % sys.getdefaultencoding() + if len(sys.argv) < 3: print "Usage: python converter.py " sys.exit(0) diff --git a/tools/scs-converter/tests/test.scs b/tools/scs-converter/tests/test.scs index 6fa3fc43..468f531c 100644 --- a/tools/scs-converter/tests/test.scs +++ b/tools/scs-converter/tests/test.scs @@ -1,4 +1,4 @@ -1 | "test" | 667;; +1 | test | 667;; 5 -> 6: 7: tr; ty;; 6 <- 8: re; gg; hj;; From d9a8670bcfe4476acd4fa154c854f637fb3b9c14 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Tue, 4 Dec 2012 01:52:03 +0300 Subject: [PATCH 08/84] work on scs-tools --- tools/scs-converter/builder.py | 41 +++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/tools/scs-converter/builder.py b/tools/scs-converter/builder.py index 2a6083bb..265419f3 100644 --- a/tools/scs-converter/builder.py +++ b/tools/scs-converter/builder.py @@ -24,7 +24,9 @@ import os, sys, shutil import codecs import converter -from sc_memory import * +import ctypes +from pysc import * +from pysc import _sc_addr encoding = "utf-8" reload(sys) @@ -41,7 +43,7 @@ } # mapping of set identifiers into sc-types -arcIdtfToType = { +idtfToType = { u"sc_edge_const": sc_type_edge_common | sc_type_const, u"sc_edge_var": sc_type_edge_common | sc_type_var, u"sc_arc_common_const": sc_type_arc_common | sc_type_const, @@ -59,6 +61,9 @@ u"sc_arc_access_var_neg_temp": sc_type_arc_access | sc_type_var | sc_type_arc_neg | sc_type_arc_temp, u"sc_arc_access_const_fuz_temp": sc_type_arc_access | sc_type_const | sc_type_arc_fuz | sc_type_arc_temp, u"sc_arc_access_var_fuz_temp": sc_type_arc_access | sc_type_var | sc_type_arc_fuz | sc_type_arc_temp, + + # nodes + u"sc_node_const_norole": sc_type_node | sc_type_const | sc_type_node_norole, } # mapping identifiers into sc-addrs @@ -235,7 +240,13 @@ def generateSystemIdentifier(el_addr, idtf): idtf_data = str(idtf) stream = sc_stream_memory_new(idtf_data, len(idtf_data), SC_STREAM_READ, False) + assert stream is not None + + results_list = None + results_count = None + idtf_link = None + idtf_link = sc_memory_link_new() sc_memory_set_link_content(idtf_link, stream) sc_stream_free(stream) @@ -255,6 +266,7 @@ def generateIdentifiers(): nrel_idtf_str = u'nrel_system_identifier' nrel_idtf_data = str(nrel_idtf_str) + sc_helper_init() try: nrel_idtf_addr = sc_addrs[nrel_idtf_str] @@ -264,7 +276,7 @@ def generateIdentifiers(): assert nrel_idtf_addr is not None for idtf, addr in sc_addrs.items(): - + # extract object identifier system_idtf = idtf if checkIdtfWithPrefix(system_idtf): @@ -273,12 +285,22 @@ def generateIdentifiers(): if system_idtf.startswith('.') or (system_idtf.startswith('"') and system_idtf.endswith('"')): continue + # todo check if identifier doesn't exist + addr = _sc_addr() + addr.offset = 0 + addr.seg = 0 + + # temporary hack + if system_idtf == nrel_idtf_str: + continue + print "\tSetup for %s" % idtf assert addr is not None # generate identifier relation generateSystemIdentifier(addr, system_idtf) + sc_helper_shutdown() # ------------------------------------------------ @@ -296,6 +318,9 @@ def generateIdentifiers(): input_path = sys.argv[1] output_path = sys.argv[2] + if os.path.exists(output_path): + shutil.rmtree(output_path) + sc_memory_initialize(output_path) conv = converter.Converter() @@ -316,6 +341,13 @@ def generateIdentifiers(): object = triple[2] predicate = triple[1] + # if first element in triple is an element type set, then change type of sc-element + if idtfToType.has_key(subject): + # todo add types conflict checking + sc_types[object] = idtfToType[subject] + resolveScAddr(object) + continue + resolveScAddr(subject) resolveScAddr(object) @@ -374,8 +406,7 @@ def generateIdentifiers(): for idtf in sc_arcs.iterkeys(): print "Arc %s wasn't created" % idtf - - + all_count = created_links + created_nodes + created_arcs print "Statistics:" print "\tCreated nodes: %d (%.03f%%)" % (created_nodes, float(created_nodes) / all_count * 100) From a0a5c6d4874074085679cca409993876e5e17f5f Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Tue, 4 Dec 2012 01:52:38 +0300 Subject: [PATCH 09/84] rename scs-converter into scs --- tools/{scs-converter => scs}/builder.py | 0 tools/{scs-converter => scs}/converter.py | 0 tools/{scs-converter => scs}/parser.py | 0 tools/{scs-converter => scs}/tests/test.scs | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename tools/{scs-converter => scs}/builder.py (100%) rename tools/{scs-converter => scs}/converter.py (100%) rename tools/{scs-converter => scs}/parser.py (100%) rename tools/{scs-converter => scs}/tests/test.scs (100%) diff --git a/tools/scs-converter/builder.py b/tools/scs/builder.py similarity index 100% rename from tools/scs-converter/builder.py rename to tools/scs/builder.py diff --git a/tools/scs-converter/converter.py b/tools/scs/converter.py similarity index 100% rename from tools/scs-converter/converter.py rename to tools/scs/converter.py diff --git a/tools/scs-converter/parser.py b/tools/scs/parser.py similarity index 100% rename from tools/scs-converter/parser.py rename to tools/scs/parser.py diff --git a/tools/scs-converter/tests/test.scs b/tools/scs/tests/test.scs similarity index 100% rename from tools/scs-converter/tests/test.scs rename to tools/scs/tests/test.scs From 2dd8d21a1633244e38b63831f4d823fd51bc6ccd Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Fri, 7 Dec 2012 01:01:30 +0300 Subject: [PATCH 10/84] fixed some bugs in scs tools; added fs links support --- tools/scs/builder.py | 16 +++++++--------- tools/scs/converter.py | 28 ++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index 265419f3..8db820bb 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -194,18 +194,20 @@ def createNodeOrLink(elIdtf, elType): # setup link data path = elIdtf[1:-1] - path = path.replace("file://", "") + path = unicode(path.replace("file://", "")) if conv.link_contents.has_key(path): data = str(conv.link_contents[path]) stream = sc_stream_memory_new(data, len(data), SC_STREAM_READ, False) - else: - path = resolveLinkPath(elIdtf) - stream = sc_stream_file_new(path, SC_STREAM_READ) + elif conv.link_copy_contents.has_key(path): + cont_path = str(conv.link_copy_contents[path]) + stream = sc_stream_file_new(cont_path, SC_STREAM_READ) if stream is None: print "Can't setup content from path %s" % path else: - sc_memory_set_link_content(addr, stream) + res = sc_memory_set_link_content(addr, stream) + if res != SC_OK: + print "Can't setup link data for %s" % (str(elIdtf)) sc_stream_free(stream) elif elType & sc_type_node: @@ -285,10 +287,6 @@ def generateIdentifiers(): if system_idtf.startswith('.') or (system_idtf.startswith('"') and system_idtf.endswith('"')): continue - # todo check if identifier doesn't exist - addr = _sc_addr() - addr.offset = 0 - addr.seg = 0 # temporary hack if system_idtf == nrel_idtf_str: diff --git a/tools/scs/converter.py b/tools/scs/converter.py index c97d4d3a..d1228991 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -156,8 +156,10 @@ def __init__(self): self.comments = {} self.triples = [] - # list of contents, that need to be written + # map of contents, that need to be written self.link_contents = {} + # map of contents, that need to be copied + self.link_copy_contents = {} # synonyms map. For each key in this map we contains list of all synonym elements self.synonyms = {} @@ -168,6 +170,9 @@ def __init__(self): self.oset_count = 0 self.arc_count = 0 self.link_count = 0 + + self.process_file = None + self.process_dir = None def generate_set_idtf(self): self.set_count += 1 @@ -241,7 +246,16 @@ def processSimpleIdentifierGroup(self, group): self.resolve_identifier(group) def processUrlGroup(self, group): - pass + """Process url to link content data + """ + data_path = group.value[1:-1] + if data_path.startswith(u"file://"): + data_path = data_path.replace(u"file://", u"") + + link_idtf = self.generate_link_idtf() + path, tail = os.path.split(self.process_file) + self.link_copy_contents[link_idtf] = os.path.join(path, data_path) + group.value = '"file://%s"' % link_idtf def processKeywordGroup(self, group): return group @@ -437,6 +451,7 @@ def parse_string(self, data): def parse_directory(self, path): """Parse specified directory """ + self.process_dir = path for root, dirs, files in os.walk(path): print root for f in files: @@ -447,6 +462,7 @@ def parse_directory(self, path): continue file_path = os.path.join(root, f) + self.process_file = file_path self.comments[len(self.triples)] = file_path @@ -481,6 +497,14 @@ def write_to_fs(self, path): f = open(os.path.join(path, str(num)), "w") f.write(data) f.close() + + # copy contents + for num, src_path in self.link_copy_contents.items(): + src = open(src_path, "r") + dst = open(os.path.join(path, str(num)), "w") + dst.write(src.read()) + src.close() + dst.close() if __name__ == "__main__": From 55efd78964c300d2e85806ff7eafb44099e0f4ce Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Mon, 10 Dec 2012 00:28:03 +0300 Subject: [PATCH 11/84] add support of variable sc-element --- tools/scs/builder.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index 8db820bb..529c39c4 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -155,6 +155,8 @@ def determineElementType(idtf, isPredicate): except: pass + isVariable = idtf.startswith(u"_") + newType = None if checkIdtfIsLink(idtf): newType = sc_type_link @@ -168,6 +170,11 @@ def determineElementType(idtf, isPredicate): else: newType = sc_type_node + if isVariable: + newType = newType | sc_type_var + else: + newType = newType | sc_type_const + if oldType is not None: # determine if new element type more common then old type if newType == (newType & oldType): From 4c4912864340d33b5bfd5447e36b114589e9ca85 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Wed, 16 Jan 2013 23:10:26 +0300 Subject: [PATCH 12/84] update to new sc-memory initialize function --- tools/scs/builder.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index 529c39c4..0b906534 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -63,7 +63,9 @@ u"sc_arc_access_var_fuz_temp": sc_type_arc_access | sc_type_var | sc_type_arc_fuz | sc_type_arc_temp, # nodes - u"sc_node_const_norole": sc_type_node | sc_type_const | sc_type_node_norole, + u"sc_const": sc_type_const, + u"sc_var": sc_type_var, + u"sc_node_norole_relation": sc_type_node | sc_type_node_norole, } # mapping identifiers into sc-addrs @@ -213,7 +215,7 @@ def createNodeOrLink(elIdtf, elType): print "Can't setup content from path %s" % path else: res = sc_memory_set_link_content(addr, stream) - if res != SC_OK: + if res != SC_RESULT_OK: print "Can't setup link data for %s" % (str(elIdtf)) sc_stream_free(stream) @@ -326,7 +328,7 @@ def generateIdentifiers(): if os.path.exists(output_path): shutil.rmtree(output_path) - sc_memory_initialize(output_path) + sc_memory_initialize(output_path, "") conv = converter.Converter() conv.parse_directory(input_path) @@ -349,7 +351,7 @@ def generateIdentifiers(): # if first element in triple is an element type set, then change type of sc-element if idtfToType.has_key(subject): # todo add types conflict checking - sc_types[object] = idtfToType[subject] + sc_types[object] = sc_types[object] | idtfToType[subject] resolveScAddr(object) continue @@ -419,4 +421,4 @@ def generateIdentifiers(): print "\tCreated arcs: %d (%.03f%%)" % (created_arcs, float(created_arcs) / all_count * 100) print "\tTotal: %d" % all_count - print "" \ No newline at end of file + print "" From 797f979131f20a40c5d551656e0a0977ae4b9bde Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Sat, 19 Jan 2013 01:05:38 +0300 Subject: [PATCH 13/84] added contour support --- tools/scs/converter.py | 54 +++++++++++++++++++++++++++++----------- tools/scs/tests/test.scs | 7 ++++++ 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/tools/scs/converter.py b/tools/scs/converter.py index d1228991..d896f774 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -160,6 +160,7 @@ def __init__(self): self.link_contents = {} # map of contents, that need to be copied self.link_copy_contents = {} + self.contents_copy_link = {} # synonyms map. For each key in this map we contains list of all synonym elements self.synonyms = {} @@ -167,6 +168,7 @@ def __init__(self): self.aliases = {} self.set_count = 0 + self.contour_count = 0 self.oset_count = 0 self.arc_count = 0 self.link_count = 0 @@ -178,6 +180,10 @@ def generate_set_idtf(self): self.set_count += 1 return ".set_%d" % self.set_count + def generate_contour_idtf(self): + self.contour_count += 1 + return ".contour_%d" % self.contour_count + def generate_oset_idtf(self): self.oset_count += 1 return ".oset_%d" % self.oset_count @@ -251,11 +257,16 @@ def processUrlGroup(self, group): data_path = group.value[1:-1] if data_path.startswith(u"file://"): data_path = data_path.replace(u"file://", u"") - - link_idtf = self.generate_link_idtf() - path, tail = os.path.split(self.process_file) - self.link_copy_contents[link_idtf] = os.path.join(path, data_path) - group.value = '"file://%s"' % link_idtf + + path, tail = os.path.split(self.process_file) + abs_path = os.path.join(path, data_path) + if self.contents_copy_link.has_key(abs_path): + group.value = '"file://%s"' % self.contents_copy_link[abs_path] + else: + link_idtf = self.generate_link_idtf() + self.link_copy_contents[link_idtf] = abs_path + self.contents_copy_link[abs_path] = link_idtf + group.value = '"file://%s"' % link_idtf def processKeywordGroup(self, group): return group @@ -310,6 +321,7 @@ def processAliasGroup(self, group): def processContentGroup(self, group): """Store link content for saving """ + result = None if len(group.value) > 1 and group.value[0] == u'*' and group.value[-1] == u'*': data = group.value[1:-1] @@ -317,7 +329,7 @@ def processContentGroup(self, group): converter = Converter() converter.link_contents = self.link_contents converter.synonyms = self.synonyms - converter.triples = self.triples + #converter.triples = self.triples converter.aliases = self.aliases converter.set_count = self.set_count converter.oset_count = self.oset_count @@ -328,21 +340,33 @@ def processContentGroup(self, group): self.link_contents = converter.link_contents self.synonyms = converter.synonyms - self.triples = converter.triples + self.triples.extend(converter.triples) self.aliases = converter.aliases self.set_count = converter.set_count self.oset_count = converter.oset_count self.arc_count = converter.arc_count self.link_count = converter.link_count + + # collect all created sc-elements to append them into contour + objects = [] + for triple in converter.triples: + for idx in xrange(len(triple)): + if not triple[idx] in objects: + objects.append(triple[idx]) - # todo add arcs into contour elements - - - link_idtf = self.generate_link_idtf() - self.link_contents[link_idtf] = group.value - group.value = '"file://%s"' % link_idtf + contour = self.generate_contour_idtf() + for obj in objects: + self.append_sentence(contour, self.generate_arc_idtf('->', False), obj, False) + + group.value = contour + result = contour + else: + link_idtf = self.generate_link_idtf() + self.link_contents[link_idtf] = group.value + group.value = '"file://%s"' % link_idtf + result = link_idtf - return link_idtf + return result def processSetGroup(self, group): """Process set @@ -453,7 +477,7 @@ def parse_directory(self, path): """ self.process_dir = path for root, dirs, files in os.walk(path): - print root + #print root for f in files: # skip none scs files diff --git a/tools/scs/tests/test.scs b/tools/scs/tests/test.scs index 468f531c..26406b4a 100644 --- a/tools/scs/tests/test.scs +++ b/tools/scs/tests/test.scs @@ -10,4 +10,11 @@ el1 _<=> el2;; file => nrel_system_identifier: ["file"] (* <- string; russian;; <~ opened;;*);; +_contour -> [* + a -> b;; + v -> c (* <- d;; *);; + +*];; + +"file://image.png" <- format_png;; From ee9f629e7c4b5a16fd83d6d658aa4c6d9e39cbed Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Tue, 22 Jan 2013 02:01:43 +0300 Subject: [PATCH 14/84] fixed bug with contours and arc types --- tools/scs/builder.py | 10 +++++++--- tools/scs/converter.py | 2 ++ tools/scs/parser.py | 2 +- tools/scs/tests/test.scs | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index 0b906534..9b001611 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -393,7 +393,7 @@ def generateIdentifiers(): addr = sc_memory_arc_new(arc.type, begin_addr, end_addr) sc_addrs[idtf] = addr created_list.append(idtf) - + global created_arcs created_arcs += 1 @@ -410,9 +410,13 @@ def generateIdentifiers(): sc_memory_shutdown() # write list of arcs, that wasn't created - for idtf in sc_arcs.iterkeys(): + for idtf, arc in sc_arcs.items(): print "Arc %s wasn't created" % idtf - + try: + print "Begin: %s" % str(sc_addrs[arc.begin]) + print "End: %s" % str(sc_addrs[arc.end]) + except: + continue all_count = created_links + created_nodes + created_arcs print "Statistics:" diff --git a/tools/scs/converter.py b/tools/scs/converter.py index d896f774..0ccf572c 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -350,6 +350,8 @@ def processContentGroup(self, group): # collect all created sc-elements to append them into contour objects = [] for triple in converter.triples: + if triple[0] in arc_types.values() or triple[0] in arc_keynodes.values(): + continue for idx in xrange(len(triple)): if not triple[idx] in objects: objects.append(triple[idx]) diff --git a/tools/scs/parser.py b/tools/scs/parser.py index 6427b405..787b0646 100644 --- a/tools/scs/parser.py +++ b/tools/scs/parser.py @@ -364,7 +364,7 @@ def syntax(): syntax = None - name = Word(srange(u"[a-zA-Z0-9_#]"), srange(u"[a-zA-Z0-9_#]")).setParseAction(SimpleIdentifierGroup).setName("Name") + name = Word(srange(u"[\.a-zA-Z0-9_#]"), srange(u"[\.a-zA-Z0-9_#]")).setParseAction(SimpleIdentifierGroup).setName("Name") url = QuotedString(quoteChar='"', unquoteResults=False).setParseAction(UrlGroup).setName("Url") simpleIdtf = name ^ url diff --git a/tools/scs/tests/test.scs b/tools/scs/tests/test.scs index 26406b4a..2bfe1af4 100644 --- a/tools/scs/tests/test.scs +++ b/tools/scs/tests/test.scs @@ -14,7 +14,7 @@ _contour -> [* a -> b;; v -> c (* <- d;; *);; - + .cnt -> g;; *];; "file://image.png" <- format_png;; From d04b37cdb7429a04c1301228a3a1fe99178adcee Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Tue, 29 Jan 2013 01:32:00 +0300 Subject: [PATCH 15/84] fixed bug with const | var types --- tools/scs/builder.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index 9b001611..5c10d16b 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -293,7 +293,7 @@ def generateIdentifiers(): if checkIdtfWithPrefix(system_idtf): system_idtf = splitIdtf(system_idtf)[1] - if system_idtf.startswith('.') or (system_idtf.startswith('"') and system_idtf.endswith('"')): + if system_idtf.startswith('.') or (system_idtf.startswith('"') and system_idtf.endswith('"')) or system_idtf.startswith('_.'): continue @@ -350,8 +350,14 @@ def generateIdentifiers(): # if first element in triple is an element type set, then change type of sc-element if idtfToType.has_key(subject): + + idtf_type = idtfToType[subject] + new_type = sc_types[object] | idtf_type # todo add types conflict checking - sc_types[object] = sc_types[object] | idtfToType[subject] + if (idtf_type & sc_type_constancy_mask != 0): + new_type = (idtf_type & sc_type_constancy_mask) | (new_type & ~sc_type_constancy_mask) + sc_types[object] = new_type + resolveScAddr(object) continue @@ -389,6 +395,8 @@ def generateIdentifiers(): except: continue + if (arc.type & sc_type_const and arc.type & sc_type_var): + print "Invalid type %s" % idtf # create new arc addr = sc_memory_arc_new(arc.type, begin_addr, end_addr) sc_addrs[idtf] = addr From 437937ac0852076cfaae4431eb601fa8c97750f1 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Mon, 4 Feb 2013 00:37:43 +0300 Subject: [PATCH 16/84] disable sc_helper initialization --- tools/scs/builder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index 5c10d16b..5eef26c6 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -277,7 +277,7 @@ def generateIdentifiers(): nrel_idtf_str = u'nrel_system_identifier' nrel_idtf_data = str(nrel_idtf_str) - sc_helper_init() + #sc_helper_init() try: nrel_idtf_addr = sc_addrs[nrel_idtf_str] @@ -307,7 +307,7 @@ def generateIdentifiers(): # generate identifier relation generateSystemIdentifier(addr, system_idtf) - sc_helper_shutdown() + #sc_helper_shutdown() # ------------------------------------------------ From ede88fc096034204052ef52482299bb709721480 Mon Sep 17 00:00:00 2001 From: deniskoronchik Date: Thu, 7 Feb 2013 17:18:26 +0300 Subject: [PATCH 17/84] add = support --- tools/scs/convert.bat | 1 + tools/scs/converter.py | 79 ++++++++++++++++--------- tools/scs/{parser.py => scs_parser.py} | 5 +- tools/scs/tests/image.png | Bin 0 -> 24361 bytes tools/scs/tests/test.scs | 21 +++++++ 5 files changed, 75 insertions(+), 31 deletions(-) create mode 100644 tools/scs/convert.bat rename tools/scs/{parser.py => scs_parser.py} (99%) create mode 100644 tools/scs/tests/image.png diff --git a/tools/scs/convert.bat b/tools/scs/convert.bat new file mode 100644 index 00000000..0ea96118 --- /dev/null +++ b/tools/scs/convert.bat @@ -0,0 +1 @@ +converter.py tests output \ No newline at end of file diff --git a/tools/scs/converter.py b/tools/scs/converter.py index 0ccf572c..e73dc353 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -27,7 +27,7 @@ import re, sys, traceback from pyparsing import Word, Literal, Forward, Regex, Group, ZeroOrMore, SkipTo, ParserElement from pyparsing import OneOrMore, srange, Keyword, QuotedString, ParseResults, Optional, cStyleComment, ParseException -import parser +import scs_parser from sre_parse import parse_template ParserElement.enablePackrat() @@ -137,20 +137,20 @@ class Converter: def __init__(self): self.group_processors = { - parser.AliasGroup: self.processAliasGroup, - parser.KeywordGroup: self.processKeywordGroup, - parser.SimpleIdentifierGroup: self.processSimpleIdentifierGroup, - parser.UrlGroup: self.processUrlGroup, - parser.ContentGroup: self.processContentGroup, - parser.SetGroup: self.processSetGroup, - parser.OSetGroup: self.processOSetGroup, - parser.TripleGroup: self.processTripleGroup, - parser.SimpleSentenceGroup: self.processSimpleSentenceGroup, - parser.SynonymGroup: self.processSynonymGroup, - parser.SentenceGroup: self.processSentenceGroup, - parser.IdtfWithIntGroup: self.processIdtfWithIntGroup, - parser.InternalGroup: self.processInternalGroup, - parser.InternalListGroup: self.processInternalListGroup + scs_parser.AliasGroup: self.processAliasGroup, + scs_parser.KeywordGroup: self.processKeywordGroup, + scs_parser.SimpleIdentifierGroup: self.processSimpleIdentifierGroup, + scs_parser.UrlGroup: self.processUrlGroup, + scs_parser.ContentGroup: self.processContentGroup, + scs_parser.SetGroup: self.processSetGroup, + scs_parser.OSetGroup: self.processOSetGroup, + scs_parser.TripleGroup: self.processTripleGroup, + scs_parser.SimpleSentenceGroup: self.processSimpleSentenceGroup, + #scs_parser.SynonymGroup: self.processSynonymGroup, + scs_parser.SentenceGroup: self.processSentenceGroup, + scs_parser.IdtfWithIntGroup: self.processIdtfWithIntGroup, + scs_parser.InternalGroup: self.processInternalGroup, + scs_parser.InternalListGroup: self.processInternalListGroup } self.comments = {} @@ -217,9 +217,9 @@ def resolve_identifier(self, group): return self.aliases[key] alias = str(group) - if isinstance(group, parser.OSetGroup): + if isinstance(group, scs_parser.OSetGroup): alias = self.generate_oset_idtf() - elif isinstance(group, parser.SetGroup): + elif isinstance(group, scs_parser.SetGroup): alias = self.generate_set_idtf() self.aliases[key] = alias @@ -229,7 +229,16 @@ def resolve_identifier(self, group): def append_synonyms(self, idtf1, idtf2): """Appends two identifiers as synonyms into map """ - pass + if self.synonyms.has_key(idtf1): + return + + self.synonyms[idtf1] = idtf2 + + def resolve_synonym(self, idtf): + if self.synonyms.has_key(idtf): + return self.resolve_synonym(self.synonyms[idtf]) + + return idtf def check_predicate_mirror(self, predicate): @@ -423,16 +432,20 @@ def processSentenceGroup(self, group): self.parse_tree(obj) # resolve object identifier obj_idtf = self.resolve_identifier(obj) - - # connect subject with object - arc_idtf = self.generate_arc_idtf(predicate) - self.append_sentence(subject_idtf, arc_idtf, obj_idtf, self.check_predicate_mirror(predicate)) - - # connect attributes - for attr in attributes: - self.parse_tree(attr) - attr_idtf = self.resolve_identifier(attr) - self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) + + + if predicate == u'=': + self.append_synonyms(obj_idtf, subject_idtf) + else: + # connect subject with object + arc_idtf = self.generate_arc_idtf(predicate) + self.append_sentence(subject_idtf, arc_idtf, obj_idtf, self.check_predicate_mirror(predicate)) + + # connect attributes + for attr in attributes: + self.parse_tree(attr) + attr_idtf = self.resolve_identifier(attr) + self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) def processSynonymGroup(self, group): pass @@ -467,8 +480,16 @@ def parse_string(self, data): """Parse specified string """ try: - result = parser.syntax().parseString(data.decode('utf-8'), parseAll = True) + result = scs_parser.syntax().parseString(data.decode('utf-8'), parseAll = True) self.parse_tree(result) + + # process synonyms + new_triples = [] + for subj, predicate, obj in self.triples: + new_triples.append((self.resolve_synonym(subj), predicate, self.resolve_synonym(obj))) + self.triples = new_triples + + except ParseException, err: print err.line print " "*(err.column-1) + "^" diff --git a/tools/scs/parser.py b/tools/scs/scs_parser.py similarity index 99% rename from tools/scs/parser.py rename to tools/scs/scs_parser.py index 787b0646..cf328183 100644 --- a/tools/scs/parser.py +++ b/tools/scs/scs_parser.py @@ -55,7 +55,8 @@ u'<|~', u'~/>', u'A|97p|wHRe)&N=(+y`Sg##B&Uh{~$?#`v?~Tfe^ftf-6EGD8=As5*rh|(^Wai z4gNxR5PheF4gPpy8zI2&IJQ#i4iE@o7xD)smWhxG0-=MvgNrD+Chsk{X(_GWVI2+o z=`V@c8Y^INW4KhleaFSprdYTup?&7wed_#suJqR!S9M-d(&Eo=4Y5x%R4lZi4lEA` z=0)u=lqA|Q;5<^gqjEm47&|EpevAyN5euHZI*YL zg!RP^2p_)`3TX;OtyAOA`26)zOlrXK1&>^2adGXK@dIcAzF&s1Ck$THaKfM?a4l34 zyE#>#mJ-XNb9T$Ga}nkjk^agc2oBE;$uKsG%_QUeO-_F><`N-fg1CmDMY;rY`NIx})rR%zz zw$eO&{?J|eR()MGq+atY*mv$6L5p$Vs0Ei#ap>K zUz?Uab$u$TeJ#}<>&_rcrq<~09m0EKI_^OOgGqPTjZ4^9T~=A~t@oH~HGB_Xul268 z-F@&%x^i^$=wlu+(@KebyIE<*<<-w9VtUc1YFVO1=#cEC@nwm1+1Kj)FOEMELtlnw zVMS!`ABx%5GuOTwial%lP*r(m{>1?bO(3bM3h(+gOBrv+Uu9B5jYX@;Ha`|ef4B3@ z%vw86L+OB#T*g8WmxfW*OdM*Xi}a0|(=cq3&j~f0q4q)|9MvWnD66JYqsK4=}VvVNM=|A&e`QIfP@`3jSH={+P7G`;Q zm}~#iKjh9ww@lt?#Yv&|j8No+#^mcu4@m75?;VM3#-C0$s`0HvWg6CLq+&r~xQwCY zrAKmLe{Szz`%pjQEJO5Vmgj)oG)897o#W)<`FNA&3CuuxU`t44SX^eVb$*mXY#wX* z2MsOqyQ}3nIS?qUrR9X=?r`r5X4((#-it%mh~eKb7;4*yqLRLG`Cfnb6cg@>(Y`NT z?_0QAkiJFIbB#gSu5FwU?C{?hj)SXaMR3Nrf0+7lOZ#454A!9a)fOVQy<_5cYPE9@ z9m+S)uM#EjgJ1U!w_(C8@+mZ)!SPs%Gz_AOpFPa@`mxNWj@1Zxm|F??*s6azrthC- z;tdm2e_@`($AQ=jBHx7^iOOC)ViLnEV>Tf%`LVR9Lv70umR0^rsoy3N5uT6HB9>T6 zMyUp|rKi1y(>-Y;fDm(-vKzc<*g=*48am=ujfMxgIU3{galF9rMNO8%N_Upjo0x2a zxIo?(24u`|Uh-h~#UiqZzwbUP({lwRH7m0|Oj# z?&VxmKqy?4yoQwy86f+imxkTNTtKPzW(Oz0ap7~KXf`S~&PA+-iJ#p+-{q)!lKI3- zpJap#rPx4M{&W0}v9}P-3%v)JBJGez1Asw?p6JBEUNN1-i8uIq09>^!8crB1dX zIwc7Q{akeR!)-c4W?V#uF-%2A18X8R^?O2#R7=KAgrJEX*O%vY?}`Z_Z}Vsk&|UOg z=p_!rgX7MhAHg8Ad?PL}ntbWxH z2BUn}Obmn2Z$|1Hs&Q>N4`PkqAqqGoUk2e2CoCMVDZe-i>5`WeX3`%N zT;;XUNWjU0z^LF5kalNh*9=(y7}8qT!pFXHmJ=ONQ0-tou_S)J!NA?7YGJ2jqx+~> zIz0RG+uU_05fk}8)oL{!edh-LIB(?%jr>h5L$SiniYtNWl8^KP6qWL~|LBVFC-l~M zq*y9W1C{cPA99#xmrQ(54e8n`>BM}vJjnMJR~1%ahR;G0U&a5|SvtD_kIg6%+-^he z+rx$rv;RD$Y>xUpp!E-HFXW^G%YJH4an~_DS{-^}-sg6^S!7w2_vR_0!NuUJ@ZOnVj#*zm1c64dW4c!q@=_X7WQdZu5!aa2c6L8r*s! zk5;vPyR`qc9qG+Dr69I+uxH}@`;KJtrHkKuANRI?tAUH!`4Pgks-=I|HYo!qWO+Mi z>q5su60?$k9pZ*<=(61aK~s2Wy`$4`&{p6%OJ0l9Vdv8o7!|nXf-ibWvw)c)u}i4D z5R8!gGw<4^?6Xo36X*wGr{G{;6r9{p+fA1&CTPt|;~6pyeUGUj{yi7j9ooiQFge+z zx=qvS3t~UaPuWi7?LL*4TN?71EDrqjK)hJaCD&i!bEsR|b9I)7UpqC{%fknZ7t@z?_2p_JMH4$tl!d4)1G8R?;w0WxAK;7>iP9F%jNbq8y3DnT%SbX%PVa)n^T)b3X7yuV;S1le zY6kb$1<-BE;H^mo+iGW(1{nZi+D8L$|1DMP9(hVI55=2I)JHY`#0W#Z3rX1T=?sl9 zfWd;}%0q}bE^d5|YBnk*XgPBy*t=XjQ(t;--F!MPH=oJ=ij5jd{yJ?VKZLB1!{h(ViOqbV<#m?xKtnl=ecgjY%+`cToDG7 zYu>!PngS?4vS1^3E-o&$js9Gu$N|Xy2|%-8%IUAhnJ(#FG_#_%*F0%!&`SfOB1}0| zB;ZC@(}HG3&MGx(Q@3bMOWWO;nzAZo9S-}?3~UZy@-G{u4kTs1QlBnstiK1VOR9-&lhXeMTJruY>;26CnSc z_l?z;`7@cuO>%rVt$po^{4vg-INi@*W6>J;oqDop0@xRyqn1n#y^ntfe-@r0E>T{) z%x;p))=^Gn?mqWnNg#@#p8x1)z0aEL{pOL@HBOnqa|o2O%>~oKd7wgNE#q51CR$d;YKtR8sp4x z#HHkwfjZtUeeK=r(fj93JJ&9b&PNy!z^>Lxi)kh84=2Fpt*a=*a>G$G&`Y`x(__3$%qxP~gd zih=s%4l)D>u>da9u5+T4X7vsc`kW?u5fEhsH|_6@SXkQPHl2W+0%m?y6aGzRacQc8dQ7|A%N6Nmu-hZy(@KRvP^hY*p)*Z9etrc?=I9mtQ&M z4499+D796`+lIfx)ai3AT}$4?2hAr{dWX zJYi=w%DPLh98(HG5~;!PA!MVvFewG2!V?QijEzVa^{jO{*56cJHZxvYz60O4OCQ#7 ztg^8Q+(Gosmw7}dra(|C=Y#`}7olpY&6vV}tUE)#;G26`YO=vy@q5CZ*KAv*%lu;> z#N->A#f(;!e+mjh8P?Ztb9gZt2dlk#1D1a!!#)TivVDT+!)LFOf<~&GA5~fwI#R-o zOk8EdWIniy!UENP?GJxG5I?)@$esp#iG)Lt2a04#mXApi2-|=g{>6s8Xrm;62&;ytkcDH*GLqS#iRNN3pklm^Aq+&X+a|<46AND^ z++*$9N*N_~AruYtUqXX&(*~0zB)9p_U6?-Ywiq$?r~2RYzzjD=X4iTxL=3qb9KFcRtor?k(#3|uR)2QKSqoww6JVHFVn7onMB;>2a3U<^uW zBDO#E8i+4by^jZI>I(?%%=AnOzI8WSWxl_oCjd|BnJElpz7ya2L%PR#s>xDkxUH)2 zy?f;dZW5M}Q--W^eyd8&XRMPSF0+OoZ9{3`@GkRY(hFZpwt(TUSC+fIl%Psg+n%O( z^tcP7GNB0vhX^U)DuK(*QfS~>TJA>JUR)dRHhmIcAG}2gVgwje8^pAHqcZmpKPqFp zMur6fAww=UfG#k)qd$$yGxn+xGX#NeXqEs08WT*g6X<>C`^UbPazRYsU|Dc5$LMp# zt9ZHNr?6Mypz2Au6ZlnDGawJ!X$J_BP{mnoS(rrd@x=RhQMggXYldK)QBljIQ~-d- z^#6khm($MRa3dZHjsN}nI-<@WjzFdhDti}X2LGc>+6J%-I1S?TakxTp@K2MUbg&A? z$B2yRKOGFNthX#T>YfEdUT>zWd%k>NGf2M3YFhb@z{Y10_xygwtuyFKt6@e}eLv14 zrzgJb`5{vpPKK1|Xti!|*;2jkBcnWKO`o(JkPVQLL)E#084SyPqsI~aCMT|UIjo#m|_D`jN&Z*L=ipx zUn<8MG7iv=#V&F{Zra+{WmQ@JcUp@C5auahmR=n!q0&;xWsL(|Y4Q5cf)vl@ejJ91 z2S2UVR{5Zw9*S>7ddG7=2aYZ;->Qgy`tSDwDo(R(yS zBGz6x!{UevFO&see4rR2g~@}+6ypJ`ut@2&3tJ^a&7)0MumUPB9$0c!?rO8n1%Dn} zD=mJDo16RBR}ut;sy_e#?mx0<6PUl5TMeYt& zEW)!Zc)x~}2QJ}C3SQcjhm66Zwda>uI!)fJg*ub`C`8|5Bp}va_y2dtX|$`uUyD!j zp(QYjcc@Q88;FBPCo@FsJC_a>|8UaGZz-D*wAza#+&wZ}(HM*WpTHqTSiD$41bbJ;DrV{4%ytVtTp_|(Xp=TrQv~&K# z0j*w*CPm?hzSDA#kv@=v%YnLzl$eWxg72?;JtV;?&@lnCogPyhPe|H4PAhV4-cFiV zW<{LhcM8wSzC~EtU@$#_y&giwT--DU=XD3_*g8F4%ZCz$!3d_y#=9;L!n_}l5nLAgid-68(vT> za~=8Ma0x&`IQOl*i0ABhARt=QNR>#Lv;coRX0I9?sr=NdpDrz@#W>Jzxf#!>O$dz{ z1@A{!>jl-Z;_0!dA@%mlW_I!IF9<}qe}-|c`x~eBd#CUCP+0Dd<`DaGQRF9y)Br0s zukfOQY-V@bL+R;yY>6c17S3SOHh?U;Zn4m;2{0*ACP$_^3mxzQ`3fJ!S`%%z>^{q& zjBb}lEh10c(5xCF&EJa48_S8r77fX1v8ZD}xwag=|~E z0Dc8zOo+-@UX#)|?gud$PXT5`^q(Wt!4cR@YV@qOC`$g^KzsY_739OH{Q#=)6+Fiw!u(cpq#Oty!_TG5)if4=GVGMU~NBUkSt zJY&LNy&abcZL?ECe&z3gu%28yeU3CZ1mqdypd2qKG{}~n zS}F3VZ)`!($7$UtBJbkhk=Ow0wC0Sy_(Zd27&nvM#)g|Q9ypOSJ_{ZPs9yrVuQ-4S zpfB+#tx+7l0UQ|-p{>(b0?O9N!m*_FFKuj{7`ng_9EWjSbKG{FqJs`;vOO0Bf%2c} zfU&bN z4RyH5MS+0b)DLz5lFKoVKVE9h3KN42%?m_L9P>&%>Ux> zpEY0yXQ5gZf_$6}7fDcyH2uAZTmz#Ma9Qhjz4u~3%DDlhM^}3>4>_Y}24?E7*{EDR zN4B3r+QLkiBnb}}U@(%N5g2j;@SSK8U{P+CW-h)VH{J()mVySCU4m)#nbjnw`_9Ol zrC3(L{^^3rP#~z(Hyl$oZy?JvRY7(6%{j;oc}1b|#BJunrf#^LWX#6msOH%R@I&=^8=Vy#5dj632tOQEhu&NfPVFQ zzPRpi+3=Ffg9VNN#>?kQUN%PUM^JUTnktztKpW8c3PyWsV#ji%?0=RT{@k95`ChAO zqY1n+z(keP8DSMhR^|x^Kx!%eOp;q*3Fk=1xX#pNq)8J6%$+~HGyemy6a*-MLM199 z!?`T*od~YxGs4O9>BpxptZ$uFxM{oNEdm&7SRhg(y}st$=udL z{RJ;-%L|BYvam9U-nS|Fn^9AOhBxR^yNWj146T_`^_hr17)KQ;&mw=v@>PgXj6_Mc z_rha-1D`Z*q?+ez?b~ygnT4w=4vz~4_z}oA*mnCYnwz=y5`n#XL5u}SNk1v6+?hL* z?(QLwC*+S6KvJpT0tJPA^!fXm8>K=hzyZp6iB}+?Z7zq~~wfE#=M%z+wT0$r+%7;dR8t;T)A8cXb{Rp^gxE`SgAA z?Rdydh!%zjM|up;ln9zAHg8`PVce-95cv8R=^-zQn1hFH^rwICTH@Q{C^jTFI%hsX z1N?POeiY}Aiq?OH4QGkKO+Ea-6xd96DIo3mW*CcZBx{D&@(;Zl2r>I@ZDQcu(>QIV z_w$E^HZ>~JW`nL%nsog{59qz8lQ9G9Vgro-j^s>h(293)+L$DU($H*F8%hroFvj&} zdHtez_h6dx9`6c0Wse^~#7vg%@TwqSl**00F}0L5ns3DbF(r|ofEc7@V;s?*(REcK zQv)1e`a#ne5+Y{B3bt%jPy{Soz1t0Cl0E|BVCB?%ILl{q83yb2$oOo-|I75wdZdu5 zkL|Q;STW_0Pt0b~#&}e?ei{j*EAjCU!aca;(9LPcyq7KLqipV??nJK-o*x zp2o|6?iGtQ_uAJOhgz49o0A(>L15F(-+E&(7A?_=_ZkgExf5E9+kwHfkN(toWbO(Q zIxbV#ipBo?AMOVV1DyvV7+tyxfi0iqPCpC$PcOi|0ehLn`+B=aVo`Sl4#W3YFqz%4 zwb%;FiuH5nVk)O(P~K~t7td0|5P7TsQfWG^OA2`HD9<1ogPU?fI`55{&3g(ClD&%6 zw`^2+4ro{)FY>EP17f|tj>F+Bw2fF1TeinIGW?vnHA6sY9KI9?ra!pooa;dgitV1( zb;Z}!oYB8f4wc^46&Ks)$ z@O^f|!VM^UK>#B|=~qEc1wwzelUGXPn$o@OCD#!__+M{Q5EBMfQ|w*N$q}4r4~{`l zRX7a(bj1RAy@z!n9ZExAv3{?kvy{g=$i`|D%_6*aN4LrFBvWFoF(?Kp$DoPlk5$A+ z@PF}FO&6n{^4BU#IokI+%t&)^F<+=Y@$_{5cWqKy&C%l~*@r3jgq)@wi(GZWV-O@x zwIX$TP2T2x-NYOrm_-5*Wh+=WO=nL2?q^ap$ltwYHZGo{i$9c00?H|N-nSmMsJ*d#F>0S>4?qae-h8Y)b(D#o*9$>@5Sz?U690ic#4r6 z0w~u%8^lr4Vp8+lZHA5hY43pACP>Nl{=9;A4#`WZ5FlE(XiaA|tGy3)JG=%5cpMmCLTj3o2iJVTaOjG#Dd@Z_%hfhG{`Tnj@ z0&v2_*60I!#V8uc-~H0!hbEA(vxTc6fRXdlr0sI`1XEzf!Zo1R%g(LY$}LbbuYX8# zdbpnd!GWf~eL3n6>7f;nTmdl{oycIASBgl;eHX9|yfyx;jLB@6qdhLn@Rn}q*z$Zf zq)!0&EKi23ytO8aYx!(y7X_uOZ&kkgKP7MZRvc!2tyDQa_00zd`hP?sC~)FG4E4^Z1Q08D zv^q3Lt1O&=-UUq!31d^^u=}%1HJeBwDD1;Gqh%nDzE<>9WGmB{b6?q9jy0iG1APL| zA0p=MPP^ zf;Oa2;8>VJ5KhM2R`Ff-I|1j;d+9|yed}isI^dkE1fz~!*m9+vH1dbd*T6#~CutZ&=q@HpN))#L*(0?&44@hA@ zXz(bz6_->fDQ2(x1o-u$%p>=L1Je$!n)isjbwg=s{jA9Q%Aww0x)gP2)O#8Xg7i?o z%>eqLhw1moJ z1D-&3YO8Z^Os>E{k;U7mxfq)`$2+$NDDT5S!EbWePzDiM9J zZ0Uj%@>~$E^UnhPv#>(IjhH0_jEG1P7T0$WRIX-nNawo)x(YmFq;>jV1NHyAS5JEm zX#bx-f0lowd3W61UfcaUF`k~8$&ij`r4aIb-OBJ zPq#*2Ffx9b#PRpd%X=YkwUxCsl7aSKTH5Y(OQA@o`O%{YiCLT5t24DKGbkCeTKd~{ zBhaP;C)+!l(v@^}uBl?iMny$KE4P?nbai)szuOx@^|?g5(ctH|mm?O{V^_6{?sz26 zKfu9Vglq7svm&pY2gbb?#?}%YhauuyTFQnT|7Tin3O6^mv6{d@beWVl4>Mn6dR!cg zU4@3?GOJf~S^e8^JySfVM z>LI*Ko+MN}R?#V&*N3f=;^IN&1JCig!K}!)-rqG^@x6Xcp<1HV-b?Ef?&}MA%x90@ z-rnxIkzgth4yNQZSuXFT5qtLz$J5IzXYJ(gU%QP#O2c?LvQS{VF^{hg#2XLbk+XJn z1mPL)TId{Vw0QExvFTbLY0b>cR9K7scLn5mcz7z-BdPg2e*GeV*xp__nQ=WrWMV^X zZEbxuii4}Gxgm5cECjsP^U;yj)zz^nyO+mnN2jOoB%V)n*rE>JR7U#9sb*wnm#-0U z+EHq{yV@P&W`?+K=fofVmJ?n%-Tp0~uQc7_#jmBUy|Ta0!e4W~)gD6fe06Iy%hulB zU!zz;PcQw{=4=z6s{y&$@y?Dqi43>La>h=;B%%5E_{b!@5`W~#VY4W3v#WbA>grm@ z_1f@!Z>Dc}I6iWBGz)9C%AA3}HY$p!sku4k>(_v9-$=8wv$wpw#Qaol+0Y_(klRh3 zK_VQvl&o5H5XftT`2G^mXG12)U#(yOnAb#rzC{5 zLgDlCTCe%|GQeI$AP{fUguK47Mjfw>LH+VO*}=8|OU= zdwYAM-r4DCy|bMOu-J8lrTYg5ks$iBeD3eu=Pa-^e#sZIjXb8N-rcjedgrI2q!ifI z1>c*iB~n*c2P?WYo=*}?NF91~WSghNQY#2UOmqemAWcc~;g+ahZjHMFFMe|3OTTGG zTtDXL_V8oTPFkYv2(MXVU-HSVVg)*wMD9>mkRSg@dr7tbM8C1A>Dq*TCa=w6SDr#vHfwWFS$TOo_*U4DRQCc(CeETaBNnwoV0mJa zk~BB%Sm;Nr7n?#{7TvIDy)QGYTKW=iZg0zNS40K=4sx0t3og7=H!B8BuSk(hb2gZk zPY;yZhkiDC_Pe9MH1woRSu?7^>W5SD5>A!s+Suxf5SQt-qYftVmWsESX=GMZylT4I zDO784d}(n@UD&&{M0nmt>_e?$~j`8D`qK^PT3GVN$RJfpkOy#oE9FAFK~Cc z_S$Y0o^?*kX@cMKZ-WY~*~0HBZf-@h;-)G>z?-eid<4@lTl`GtB0rsUFbDAkswgTx zkcfEnH0k6AhmPm5_>i=z$=%Igxu%OnkLfx)25_S<=O%Nc$z_sw2sllL0<*HRtTYuA zpg#u&jKA(BChks^PPq~6&sJMH(Wt7X=TBHvft1?*wy>xOUTeK@^pmMnZ!G(r`$aEz z&rGE$hbw5g`n4DVB_j(?^SPruKRtY`uaY^Y9ZIMHT1Ijvunm_g{blZP`K$)qlM5#h_MJBJ&B8p+u3h) zJvNwWfxzYD*-q_v-D;9e%S$0JZL4j5XyR;aY|Tp-iL_agQDou@q+p2)YKtYKXjZSz zcEyWT?geXVK(&*0E&>~+&TdU2;5sfoJ`9hHdF0yN<9yG2?4IQ5N@H@GG0M1wZk}1| zk5YuL2ZHlAS-<}-t$CRE&8%3}(r;C(YBnY3`G!JG(imaCnCI6zu8+0z!k*P;OEzVsTGztY3SH3?4N$incFfBn39+M&(p>!tw7Et`TFI{4t{A8 zuT5xbYHEdH%k@Dc=C1oeJ@B>Q5)wSy7sXEm+&pWTJ%xUTlGkqRlTa7(BEWwW?wCzj+co%_e>T6axD4k_A1Sn^|5#RvDrMU?!%fyD4iv_t$AU zecZWDaY;!`v$M1BdqGyO{!T+QaVUP`L7gc;D=K+?ep%kzOW}0G=b6|{2HYivGl@r! zi}uROdnGAWp#H3ccbCr><07w{kMeY;43VOrQ(V{Y9s*!#WUnjkvv{U8K3v+g4g%Vn zT+O3MAVke#N?5+kInQFs%*-SvB@LwJch;LO)0cYxUi5IW+3xDp9NXWwuI@DwU9GRL zBLm*t{3T==Kn3J(4WsHdid^)%(DuB)y|A^jYwwAmTG`o|&0AYtT|Mf+W7FFhOmIDH zp0e9`?7QNPfJB$PgrK|NZ!ORqw%l{P@%)aaM~8X$l+rbi7Y-T8HW)MghfpUCY72y; zTVoiIVTlyK6KC{|cKFwpt%IEqkDAOM9Pu@igZvl)XMG`v_7cS&= zm2R`tG8QqV-RR8i8Zj_pS;oOY+1%80v>ZURI;HD_k=|&2v)mp4Q1s=3H~%v+r|mdT zioDVkYr=BCj#qnWXc=Cesb?=r1-vio38uMk$0l01<@G-QWZY{UuN_3Dp*G;R;H@=StJzpF=q+g%PR(+cOo$csd^Y+>o}NiNaB2 zPNtn}aIyq>674e~HLAQX$@W{RIw}Z3oM#+Sp3{mwmk<_l^0L>u@t<#kczbs=mJXU( zy8rF1u0KYQ+?2bilENT#eE;cX2OY<@_?6;xPdtfLf-Ts+Xna>n*e{k6c|Of92a)SU zixvQuQC49wpM}5cPQVk~+UofLQB+hUL2LO_iARNT#!KDUfKSPv_1LGWhU8)9!Lu>o z-6G{=pe7O1rOPnU-dQo^(Y?JRe@i4;E&f?WQ}Yn>qWnS8NM||cvrBl#QcDW7z@lkZ z%)#=*Ql-zSBA)l$`z0qymg!yEGEN1B8%9#ue$kwW~1H$K>kx%-!N^YDxljA)Hix1l1N4|CVSjy+rTyz5A*<2ii)BNFs@XfKM4$z!w3oI_r`vg}`2R@-+I(Q1CNMDhd%dIY%Q0WjwHv=(>(pNv!8@ha$*8P^489-e6REk78Vxhy?MhTCH+xLE1biy zhfY|SHcI%ejthE_Vz$&G=;`Tcd$yx&q&2p1N6c%(7U%siJiXtej7^Z_k`ebKiYEsj zMnD>--de=D>iL?I#-qjZ#VC}Lw??l2OC`8^s5i6f`F20*EyR4&rgl1g)N8@ZE{QIg z7T+#1;T*b{HQP**pPwHgRC7-JZ#F8EL$h+s&=`%;B$s83%8?#G5*C}Zt-2p+$PzW2 z>h>`?p+??${_pl61tLdhPrkkpzZ;z zaD{QIVCJ~D@zidj$49|CH9gbJK$pjy_)B_9+Ki9CB zD2P?l(wbkk1MGFA@hsb24^iJay2@E`RDQ)R#a31@fnTz7TOh0$D#vun^1UT2N42X1 zx3kp@6o&fx`b(qt>v&e z1b%00KK-&-Q)LtNE(_h#&~`zvScU0)cS`QuJ@Ji`MKc3-SZHY1Vzb9Uq}xgmdAOR> z-wIr3TzJ*6RQ`HS5Vi<0d;XbOvpTEj{M9mOC;SXS(U_+>CfnzHAeiz;jx}d)cJ3c& zH@T4*0?c+LCtl}EF^M=7Owx-~*HxUEE9-@Z72W$Qa)8^6e+vp8wV_dPTYOX4d0Ov5 z>8daFYgeN>|2g_LrV7PKi_XrVUafK$8)Tlejo)yo0Pe;&Sfp*1r z;rrt!!4%_(UEbDf&{SCAg}OJlrk7TIRAI+!vcM9YFSn+oq7qzI#=f`Is#~+z(}M{r z{#U?t?EfGZjT)@2tuFz@IHl?S@QY1QkQxmW|E;&T5HigF{oP#p%g3hEgsrQqTUTGd z0-%zMK!}x!3Ry>IXG~mNuuikPes3hT(+37|dr-p#gs!$2n$JW;M7E~O8IXwa=F$%E z=4LN|DEP%q)@*ntWJ^H1OqX+O(5~#L(voE3Qb+e!-XX?`_f}AudXKW%i9*%x<>h5e zo9Lo{D|>s)fHjxCkGGCFaO(+sV^*z^kN28|Wxr%dp|Oyim$&wvh2=JY^xem6y-TB( z#J+Wo+lo2THuht`P?468WEwZcs!+Ou{zbH=&mNGcts|)qIe7ZV-8?)D!8}v&T5DyU z6XD`^0H6&fsm!D#+yX4~$6Of^3k${u=lxKdmVddU7wFt@;z0LCY^p40)s-^+&YhI# zI7cPMhpTIAfIxLf2(ESw4B!D$(F+iAxO|FTzjqR7TG2BdW-B{yAtGL#om)nsFKI3Z zzmyeD#fQ3RbH|?s!m-P*epyLx?CgYcTTbd-pYOZ=iIK!#Y+kA~l`B#!NBvAhD8_reL09;7OLTU@arsI*SkHMUMzcgmAj;8d$yXX<>o}XNUQz<*fpI&c;xAd z`G9|nR$Hq6^*$;nipSPNr+I%+g@*>)rM0#GC#N9!Nx6+BZeTGa zI^->co&)1;-oK^TrE#F1AlX^YKy>ADny!RIkKixYQMHN$4)}5EA|0jHMbG4g>NycM{|KJ#2h*djE}~&^Z!!DK`^8n8uTq z+&Y+*OC32`I!e?!yZ>}{?>1q7c8DXt=JHfPXE9dwdQ-9aG4fF|$5sdBYt?(!cfzW8 zxBniJlN$m-?}BSh+|X%j1Z8n?@x_Z5BUJWYyafSa+Cjh4CwJKe=@s0gFmTJ5pXr>p zF!AS}dsw2T~IPMn@J$7({fT3}5C#?ZU!a8;|WHalF( zhowUS!$!TjI3!wLY{V|)s7v(w@AE{uXQJ3AcB`T&ptDl?uzbJ(`6QQcTu&XEg$sJ- z!9_QM-KUE=GPut2NgU?t`Lz{;ZqkWX-h)S4yINPxcRRjGW`3;v)e#Ie+qJ=b50rFAA%m|dN-HHmmr~9etOA%xfOcQ0{LDY^G@OpH`CmmHn46t->83*$t2mTFdpYKYQRcX2oL{043`6ZSUUCR~* z-Xc;Z$JeyHF5tmPl<5X6(Yk}b;2FB_-1P4+-sEq3acHkaj%Gkk7i+2h%@)nFZQYd5 z2{KhLZCW;)KG`2aN53y9WpcU5SSJru{_Q*S)sl;a4LmG2U+$<}Rgxr8m-219KVkaw z4rMYPvL%-8sq_52EB04KBb+3iGpT^O`eW>yL4qU~@m1I+@Y27Y zj=irqIm?~*HJVND`h4mOX|*%1F(z+c?RZ<8a~vQ$en;Ad)zOJ*!qxNcv@W(nXIb2& z4a5D&gSjNlQ1DAk?7;J!%Td3S4`b*#80c9i1uL>mm~vY#y1CMKi0%rG@`XxbN`LKK z+>JwowehTzFyFbe4Isa5<(9g(9^E$j!mi zT}150w%iyCXF1#bk7Ux~^LG~t3L9m!M85PK?b;mJFI4AsAH@dAoq0GI>L!_Q{`FgF zmm*{t4;r-!{Z!;`C` z=vS29x3cHHr0*YWH`gDR{`${ziZzl`VYvrERXe?dhy=v^{`1}K(4<;&1fhh>+ly4F z#j+FGpeO`FQ;Gb40e-3XZ@=yn$h9jN075qdDlveDRP(hZlBy#q=07DP=EEduy{d+K zzj4JD6)}JK2X{}j`3_Ye@Iz_F*HOyh&+2N!6H9bQ&!C@RDFMV!si9{@{ zuWN$F^|*)1=7qLmB~edaM7#I|Q23w8aGivcM=1a0c#1_t!}W8@$QIf*0()hi`|7BJ z*id__Afz2!8b)IL9*qrO0agXUjL!jXNCMqQDu5Hu%3KWbktZ{*N1m*Q6r@0dy>^Of zjyh)KMFYR@fWcZ2_OLsX>vLnquXFrTMT-e?KWx5`aRzJBLnC5_9WYP^cI*r%CWHJ7O=HP<4>=ao%n5qLNLZyV6WPS4> z7RFkAe8AT;tE!Z?6`$aj#7XBkqM%zfw-9y5J{>y&Pq*Nzi1ipmg8Nr5(?cO+(a3hB zbT0_7?2D#XwEeWyLJef6_wf>-gC~dmVUbT6f!+lNN~T7&MG%xhLJt^Mmj{i9%NrZHj&2W}AJ#DAvjoYWh7=e!pZMe` z{su76=X!2w(0N&07C`scz#9N*@2mz$#TbkQQ~`zS?8Bz7?BJZVkt2Z?O=$*ix%9yZ#($GX+Ube z&kSu7goER7C=N;JHB;M4+R4^{5!VKk3U;46Pl4ku5&+Sv4PIR&#pr{EuVv8L8!&%`3IQH;6`qa zCGj7>CjKFprc#!fI|Vf4rpwi^v5Nf9WVZZusXljZ%sNdJzN}?B-v5l7ZqMgHBi<5liINVCD8Ob3cx)EK zfy>9Jr2LnX-YH~B9i+fiyBq^>6U@9Ck`CIZ>%|^n1qiJV4rOjPgzvEZl<`9=7BaU z7b%iegZ*qroN@Sr2M-_+z?GyveCPoNGcKTdL4_9H3k^=3h4O@Wc%8^-7QVYg0}A|? zPny*h4}m|IE^n+v9!$2t{rvg!%j@ebdK%^NQPAg2~PY1l$$I@}k3JqKZ;>5Au^Dh+eKB}o9fJ^4MGyZN{af>yD&jItW z=`W%8ijPEPCt=4NYch^VLtXMV)db8?aZatz$PsIeUR zhrw|jAeROM#RO@}f)9WYDH25^NDPId2n!463~;`9^G3tQ=rN?qVgeV~7A&k)p`oEi ze}{$FfJEQd-|rU?fY#u&8w{o`9oQTMX>I=VO>QK>{Od&8??CYf_B%a0J28>)4ZHjK zUcRH7?7?KQ=5)CsmiP5OuaT*#SdOAdv5Fxu!nfvXOVzcPIH_PSSAWJbtpYkEuBoXB z^=*$*$dU+oE|;p*Fn(`yxEPT6=J4LHm7GmYR*!^@(gsiQZPMFOcp+=3o?<3k)9F3q zm+%@&@)XBMbEG82AHu(ASbiCGzTy`3=Kj#F{I#ma-u&q(Bi<8u7J9Zsnf{ctL04TE zZb_d|tMJf4hkkt#`uU#Ug=H}1&^q6>TdKpk=aAcwu^?zHsG>XsBa0<0)||28iAham z1^aL8;LUp}DdL=*ocEs#{-0{DG#<+TT~Encwz6e6`bjiMLPCWoA;j31vCERI*_RSR z$eJ~wNcMHCF}BFQ?;-oXhj8xs{a>7OUY*zH&CF*$JoC)+UGDq3uj~5W<%frEn@ibw zWI59OO8;?NdYw{hYMAclWi_DD)4+Jk2aGHWSYG?p62Zx0yMj6e zY5xHl9n-_nHni%z3EZn&gSMt-rlZC994uR;kWgQ?6Acv=JIGM4aZ}1-)(<>R4(6xb zCy$afYzXh+_xLo_b@Bn@re;N2cz=QZpa=@3IEHnQTg-PS=R&a^4c4CYPYtWPd*kcm zp47JVjEo=Cy^D^Yva%j6=2tX=o-L!S+$E8hMsMJuve@OhwlZ8~1SHe`7#bK(b<_7E z3GGr}ddD3+>G^cI8JU=(DQY!--9oX6d7p@bfc% z4NTv6+p?LWPDvaH$IF|S*uDSY*us;O*V@8sX)DLyS@wR$m)wP$FdGej54KZ#7rTar z0zrKIRlPrfi{{lZ+0N0cq|2+`i|3bKON#1<_k3hz^l4>*S4c=LQQWn#-4#U_44lxe zXhK$A-u{a|8>?r}E`nm^+zbNIXUB)*)AM&vvESMuRPspv-M|UY=j7zj-aYINGub94 zBn--{JsD|M;WY;7y0Bu>WzusRNOqeJ9Sx1E$)0;!!+!~gDQM<9Q{b!h_A4ikmFP}C z-hUyF)+=#3x4Q)sg@K(GAbJ_@H#%^s}3J9Dqz2U{eq&R zBEknu8z6YL4d_^fhK9!7vZ9lhmmiyfT^sNt`+b2!*cx_HU0D1rWlpO*=M9zqpd@&n_CxWiA*IeTVW5M;2@z`agzrUX} zRF<*Y|{JjpWHq~gF_78V+K?y9>$+Z~iajV^lf?(yOi{aA8J%Gl_f%*=(lhm~?}ZWY(( zV1iuroB?zd7IH;q5p%8{|5>}whIL$gZ!L^Ue1*)8hQGw9bFX)Y2Ahp zzP(N>KGoMt;h7&!xpxg!uG{%8>-uBW?t{iPxw&buB*I+XkAl&aBWQ%USIh%)oBqM2 z<<7Wbk92Td6i$^o$FEvSJYP1wmX&73aKa@hc(M1(lV*rwqt>{Iig#{O23FZMXan!2 zf$a$3IyJA;?c14}W@yM+8F(H1lWS1B#!5j!(FA(MI4(DVh{WbZK40mJH1Jmdf&3O0 z7AW%WzxMY}EiWIE(EX2($ng*h&jEwHh6$CO**8y_L@ytzgHT)NN0=0IN$O)^p{lSF zd9C9Z+OygTOh&``j7!UdQEMqHyu!9)+{=SXj~;D)9fOrE!Kz9lRi0Wc6Y0VQ@_ON{(awX zpghf}ZH8|GxHNsS>?npQ8@+?thp^Yu3dSmh{^tan#8vKHazD*()*Owmw|0bVZEXp- ztc^LhCEjtNz;l{~28wHA*jHU$%EQt_u&&oPdmi(b=roxX zVZd@E>c02QeGIS%e*3eAmfR6#rT_=q{k=aw+W2cj+7m>|bUxSAu)cWlqR=4!WH-+{ zRAl01nw;_K?!QrSIe?sifS?gdkN<@CTeofn!P1(X*%fuG)~A)rc(w-w=Z_I_cJ_N! zTdlm)aI3-tW{Ln1_9$gEHRG&Lzne#SFmrYPjXcaetn3cge8ChN1Uk3TB3Co zI@@1AuNi=yVbQR%w)WDCGF4?|D#?T2mp~~$6#OmuB`3iD%p%6FS|1NHum&?bFg#C$ zJAKX&*sToRP)Ly+kLBR~jQ=loKr(LCtYEc(bS|f*^!Ait%}C;ex9W>LZ9*+InVE&;GrbBH#V+m!vB@bXv zW-*-BVR-;mU_C6DH?jn#2Ab19&3B_H4s@`dN2Pxn`MsvpJ;Bd_7i6L-uo{>i)rU`? zMh;cFvY{s$1*S$Qim~GgYs@ zS(~vFl~Gc97Z@CDC8A|Blz)H7S~x?x+U2vroj}&iY^olo3J^DfVR5?lX=L}mH8|Ododzj*d>bQ?6EKo_bt688t=w+v ztWuJg|Hz2;IYL4X9v-rp=CJ*(FBLXJwk>r$^NNaYIB}YcbNd!mHavp&i#o0!r-)uM z3UygOdKwIg^cN8F<@U2=M3k%{q?fqZ#l#rToZ<15oK*E4GBxF<8?SBGt#l^8s+5Qy zWoib7K-k!vTwJo)fM@Rcxd_7&?HAy<(XDz(jTdF0`~x)9mvKjvwijp9mlRj5mOBy` z92jqL`tV{0Ia}MYd)igxuIeJji0!F~9~VdE!Shnm0|bJ}-byN*UVAM$27QJ0$7CXk z-#GK0lY5&Zs`FNEV^x3C7+uPs((TyuXqxlv@`KG6(Zs8Jry|(k{k5#PG<8v9|XRK zTu37i47I(LM~;=tS$jBwnvb_FIKzlyWnB(--sEH{9?gup+*6G6*L-`q!?|esUPPbm zK|)c48kJua=go}`di1bc>trH%yBkyKs?q~}q!0g+Pm~3NkdGwhdOfDD&Tp=hJXX=_ zs<0l-z%doVk2>oWl9WC&5rj=lH{J-W&fCmOtJJ`W{KJksF0%f|0K#n$NYg* zI(XvbnK7SG+c!;@hG8P5B(LW>(WI+{qqyqnO5$UE0ewwIKlSBSst9EwL>E(fXnsXY z$WI>Loi~@`&NES<3G8Z=D@Hyw`q3crQwtfpu8N9CJtmAX)ADWjFm(C+ch!KotMcit zD(WQd7z87#v^tV3HL|s_R7Xiw-I%c6cq58!N9_>Zyc(lI&LF6owCbFS9luW7!j0Hz zOB-L#n{{jDMqEfpu0Jn^uu|nnoZ&^#n9tpdyw?7e(kVJ(;E|f*Z6Q*##Sx=tEEL7M zeAc4SXpe14fd1aei<=)w>->h2Z8=?&B$f!7>}!1-%%9=*($MVU4X$?RL9ZmeUCZpm z9w(>EIsqZWX5z<6JkDZeK^a*SNZ zQT$S@v{6t{SI)j8Ew7{XO!&I^VI0K>IsG?o*=?oo{qLSM6SbYh4m@>mm+u`Ja@Eb3 zU&grbG^{Z!+)7?9MXFtGR583?(;QLcV9vm}qQt&-(}m5VOh4v)Plu2Y12<2s?R9&b z$1HtVoITlKzaqW=c$=?!Tl(7067uF-!f-4f6SY=d%bg|hkN2-mj+VA&dblr;aiQ*R zK^{#_O}#H!*z!710p`~AA*GXZt(L-?l|~QI+=`S!Vvu8SUs#gwTv36MP;A!Mg8J&G zC_#=zfsY1~1pEuX$K4$UTPjb*c!=-SY%ds#JuCY=GdGvyW2GlHnuAre8A?`pGfnqH zDc%fsd%h&^Z+pk`9aLp);+<~+D!xK#iagsm3!27(M|V=#mZ&fVkK{V+Y;Sg)W6B^X zHP@l0r7&a;w<`WlKa#ECP!>gQ+05Z&DF7Pv-wDRf1+D0 zkn^TbJ63bwTi*j#^CJ1Ew1$A?LD1K)JovgFl>D_9xmFJ!CJuXuB@z_Qhu1 z=+cPgKynagH`B2`XCKvdgzH0jEm5E*sPb^)d7MFFIqguzC)i>4_ zG4f??bmnt^b{~u-cX?Ib?v%td^$ZWME&Jx~B{R{VCAs8cXDs$%qZ!oA~ONlYEJui}uELahOg9$!;7UCBW zgl6mmBAyki{cZgT;n)d3!Yu8qHr}?kgjS9$1K2cs`9azW?sVyjO_&nHAt3^X?xo7# z)~`${zaOpJ@Q<)o_?1;11%)z+Oh;?z*8z36Syp?jxM-KDqZuw-Ni`J3GkTv&We`d# zN)iq{@^LJ1z0-+ga|SFu@ZG>abM?n3GX+} z=Gw0Iq$)4G54WISfL zZJ+KJ0e@t3rsVl)BKYGX9lPGlNUe9R6#f!^xa4wZlW=pT?N4^(Co_-G$a;{osV`mX zohxo?l65++Izz2O3GHo=NL6yUbr9q~&tDhv91&MO2-`50+K#`Ci(|-;=0I-gFmk+B79)Y@dk$T90t3}v zJqpIZ4i?imAMM%vh1$F>>*<$`b`j{)bF|(Jrb0jbB!QQ4aU(GBFIi-alUXBT$pdx&Zss0HY7yG)%7+ooC}dJg987OKX=`a=#)HCh{*@-HVj}U zp3u{*b}O--08#W8>;@E~WsP0jWsP}nb#y+#2QH$Dj0poSNKUxQsF4is+h!28W5TBs5vgFgheOy)z0v*RX#XAN+tC;PS3^z;FViOlK1 z@OLEL!JjT5BP07|CkieNBK#p3bT%#2oQ|Dz~x)dI|c9h|4I z)f&(+@bnL0b<5O=c_5R{orFL)6u zQn5`Ao-hEwWO--2uAYY!iA%lxZN~+w_mPoM4B}l7eS?FQUsy!#5I=tWs3A`dYq*l`nK_0{$)Vr4Ay<4dOdKKb@ zNa^|Yqe3acj0CP00iy;IfZ85A-E!Rg{ITsfExOq89|D7dHceY(WW4+zwKanm=Z5>9 zb>F~%o1WQspEEB(|Ja;iJ1r;XgjMUk!n#wiuwW4t6=evw({I1~tdASG5djP}weO?H z)KgPa+~7ZFJN+XUrZc67KSfbuH3;VlF2X=9nFP_Lw|BhZ;$Vs&=KAg8-XJ4$;yeC| z%t4fyo0k^{TTfX>C-~z>c9{ncFiU+|3<8Ee^pEo~m5_A4rgKzdSl_DpxeGzlzzhM) zEL}j5)VI2+tQ_S7q-6SL{@4{T(tP-+Wp2wl@S1(S<-$*Ya_lXRmh-3pu2d?7VavnD z4o!A%2jTY=Wty@w7rqLnQB4d8>a{t9THSb#|1zz*j!vsDx?$+#z|6~emIo3{rR&aI z@6)#PQD^U!(@`S~u|BQ*KOeS|$tF8!&%HR0F#I2&c8oN8B<_5>Sdqh_0)wIKn{J{8 z*|PY-H)CmNmn*9=FnZL9Yr^;Var?9=s&1jK$T|5 zZ_k>?rZQ-Yvy2*DzMR$a_?(ZSj2M5kljgg?7P+|Mzj_g*)F03b&~^i|&_^PDd~>5gZGemtm$A0&j__F|{Rh*63=-UgMTl z_0w898BJPJ5?8+k0F~&;BH~`a&aAeK$%lN2y=ztBBVMmKpwfs#zFl%d~$0b-u [* *];; "file://image.png" <- format_png;; + +contour = [* test -> y;; *] (* <- x;; *);; +contour <- y;; + + + + +Partition_SCs_code <= nrel_decomposition: + { + Partition_SCs_code_1_level; + Partition_SCs_code_2_level; + Partition_SCs_code_3_level; + Partition_SCs_code_4_level; + Partition_SCs_code_5_level; + Partition_SCs_code_6_level; + Partition_SCs_code_7_level; + Partition_scs_divider + };; + + Partition_SCs_code_1_level = + [* x -> y;; *];; \ No newline at end of file From b6b8cd53f99cb45e5939e72a6e5a20bf0b23a8a8 Mon Sep 17 00:00:00 2001 From: deniskoronchik Date: Thu, 7 Feb 2013 18:36:23 +0300 Subject: [PATCH 18/84] fixed bug with contents copying --- tools/scs/converter.py | 6 +----- tools/scs/tests/test.scs | 9 +++++++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tools/scs/converter.py b/tools/scs/converter.py index e73dc353..ed8a38fa 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -547,11 +547,7 @@ def write_to_fs(self, path): # copy contents for num, src_path in self.link_copy_contents.items(): - src = open(src_path, "r") - dst = open(os.path.join(path, str(num)), "w") - dst.write(src.read()) - src.close() - dst.close() + shutil.copyfile(src_path, os.path.join(path, str(num))) if __name__ == "__main__": diff --git a/tools/scs/tests/test.scs b/tools/scs/tests/test.scs index 311133e9..27df7639 100644 --- a/tools/scs/tests/test.scs +++ b/tools/scs/tests/test.scs @@ -8,7 +8,7 @@ set ~> < attr1: 1; attr2: 2; attr3: 3>;; el1 _<=> el2;; -file => nrel_system_identifier: ["file"] (* <- string; russian;; <~ opened;;*);; +file => nrel_system_identifier: ["fs_file"] (* <- string; russian;; <~ opened;;*);; _contour -> [* @@ -37,5 +37,10 @@ Partition_SCs_code <= nrel_decomposition: Partition_scs_divider };; + "file://image.png" = y;; + Partition_SCs_code_1_level = - [* x -> y;; *];; \ No newline at end of file + [* x -> y;; *];; + + + y -> h;; \ No newline at end of file From a8d840915151552b47732064328250476b1a2977 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Thu, 7 Feb 2013 20:45:06 +0300 Subject: [PATCH 19/84] add support of ^"file://" directive in contours --- tools/scs/converter.py | 16 +++++++++++++++- tools/scs/tests/incl.scsi | 2 ++ tools/scs/tests/test.scs | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tools/scs/tests/incl.scsi diff --git a/tools/scs/converter.py b/tools/scs/converter.py index ed8a38fa..3e029d48 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -333,6 +333,20 @@ def processContentGroup(self, group): result = None if len(group.value) > 1 and group.value[0] == u'*' and group.value[-1] == u'*': data = group.value[1:-1] + data_str = data + + if data.startswith(u'^'): + data = data[2:-1] + if data.startswith(u"file://"): + data = data.replace(u"file://", u"") + + path, tail = os.path.split(self.process_file) + abs_path = os.path.join(path, data) + f = open(abs_path, "r") + data_str = f.read() + f.close() + + # create new converter and build data converter = Converter() @@ -345,7 +359,7 @@ def processContentGroup(self, group): converter.arc_count = self.arc_count converter.link_count = self.link_count - converter.parse_string(data) + converter.parse_string(data_str) self.link_contents = converter.link_contents self.synonyms = converter.synonyms diff --git a/tools/scs/tests/incl.scsi b/tools/scs/tests/incl.scsi new file mode 100644 index 00000000..daf028ef --- /dev/null +++ b/tools/scs/tests/incl.scsi @@ -0,0 +1,2 @@ +incl1 -> incl2;; +[* debug *] <- debug_contour;; diff --git a/tools/scs/tests/test.scs b/tools/scs/tests/test.scs index 27df7639..932af9c5 100644 --- a/tools/scs/tests/test.scs +++ b/tools/scs/tests/test.scs @@ -43,4 +43,4 @@ Partition_SCs_code <= nrel_decomposition: [* x -> y;; *];; - y -> h;; \ No newline at end of file + y -> [*^"file://incl.scsi"*];; From 32db7b1589250a69db191503a67a33f4241fed97 Mon Sep 17 00:00:00 2001 From: deniskoronchik Date: Fri, 8 Feb 2013 17:34:41 +0300 Subject: [PATCH 20/84] fixed bug with contour file names resolving recursively --- tools/scs/converter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/scs/converter.py b/tools/scs/converter.py index 3e029d48..380901a4 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -358,6 +358,8 @@ def processContentGroup(self, group): converter.oset_count = self.oset_count converter.arc_count = self.arc_count converter.link_count = self.link_count + converter.process_file = self.process_file + converter.process_dir = self.process_dir converter.parse_string(data_str) From 34fe69da5ea5779d2e054b6a297e437a7670a951 Mon Sep 17 00:00:00 2001 From: deniskoronchik Date: Fri, 8 Feb 2013 17:37:59 +0300 Subject: [PATCH 21/84] fixed file names relative names --- tools/scs/converter.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/scs/converter.py b/tools/scs/converter.py index 380901a4..0bd19ad7 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -334,15 +334,17 @@ def processContentGroup(self, group): if len(group.value) > 1 and group.value[0] == u'*' and group.value[-1] == u'*': data = group.value[1:-1] data_str = data + process_file = self.process_file + process_dir = self.process_dir if data.startswith(u'^'): data = data[2:-1] if data.startswith(u"file://"): data = data.replace(u"file://", u"") - path, tail = os.path.split(self.process_file) - abs_path = os.path.join(path, data) - f = open(abs_path, "r") + process_dir, tail = os.path.split(self.process_file) + process_file = os.path.join(process_dir, data) + f = open(process_file, "r") data_str = f.read() f.close() From fc676cc82fa08bb96416ca938bf2d0ea8d6f73ba Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Sat, 9 Feb 2013 00:27:07 +0300 Subject: [PATCH 22/84] fixed bugs with contents copying in recursive inclusion --- tools/scs/builder.py | 21 +- tools/scs/converter.py | 1052 ++++++++++++++++++++-------------------- 2 files changed, 542 insertions(+), 531 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index 5eef26c6..7664fc27 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -201,15 +201,24 @@ def createNodeOrLink(elIdtf, elType): global created_links created_links += 1 + # setup link data path = elIdtf[1:-1] path = unicode(path.replace("file://", "")) - if conv.link_contents.has_key(path): - data = str(conv.link_contents[path]) - stream = sc_stream_memory_new(data, len(data), SC_STREAM_READ, False) - elif conv.link_copy_contents.has_key(path): + + #print path, addr.seg, addr.offset + stream = None + if path == u'data/link_75': + pass + if conv.link_copy_contents.has_key(path): cont_path = str(conv.link_copy_contents[path]) - stream = sc_stream_file_new(cont_path, SC_STREAM_READ) + if os.path.exists(cont_path): + stream = sc_stream_file_new(cont_path, SC_STREAM_READ) + else: + print "File doesn't exist '%s'" % cont_path + elif conv.link_contents.has_key(path): + data = str(conv.link_contents[path]) + stream = sc_stream_memory_new(data, len(data), SC_STREAM_READ, False) if stream is None: print "Can't setup content from path %s" % path @@ -301,7 +310,7 @@ def generateIdentifiers(): if system_idtf == nrel_idtf_str: continue - print "\tSetup for %s" % idtf + #print "\tSetup for %s" % idtf assert addr is not None # generate identifier relation diff --git a/tools/scs/converter.py b/tools/scs/converter.py index 0bd19ad7..b1b70d40 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -43,539 +43,541 @@ arc_id = 0 mirror_connectors = [ - u'<', - u'<..', - u'<-', - u'<=', - u'<|-', - u'" : u"sc_arc_common", - u"<" : u"sc_arc_common", - u"->": u"sc_arc_main", - u"<-": u"sc_arc_main", - u"<>": u"sc_edge", - u"..>": u"sc_arc_access", - u"<..": u"sc_arc_access", - u'<=>': u"sc_edge", - u'_<=>': u"sc_edge", - u'=>': u"sc_arc_common", - u'<=': u"sc_arc_common", - u'=>': u"sc_arc_common", - u'<=': u"sc_arc_common", - u'_->': u"sc_arc_access", - u'_<-': u"sc_arc_access", - u'-|>': u"sc_arc_access", - u'_-|>': u"sc_arc_access", - u'<|-': u"sc_arc_access", - u'_<|-': u"sc_arc_access", - u'-/>': u"sc_arc_access", - u'_-/>': u"sc_arc_access", - u'': u"sc_arc_access", - u'_~>': u"sc_arc_access", - u'<~': u"sc_arc_access", - u'_<~': u"sc_arc_access", - u'~|>': u"sc_arc_access", - u'_~|>': u"sc_arc_access", - u'<|~': u"sc_arc_access", - u'_<|~': u"sc_arc_access", - u'~/>': u"sc_arc_access", - u'_~/>': u"sc_arc_access", - u'" : u"sc_arc_common", + u"<" : u"sc_arc_common", + u"->": u"sc_arc_main", + u"<-": u"sc_arc_main", + u"<>": u"sc_edge", + u"..>": u"sc_arc_access", + u"<..": u"sc_arc_access", + u'<=>': u"sc_edge", + u'_<=>': u"sc_edge", + u'=>': u"sc_arc_common", + u'<=': u"sc_arc_common", + u'=>': u"sc_arc_common", + u'<=': u"sc_arc_common", + u'_->': u"sc_arc_access", + u'_<-': u"sc_arc_access", + u'-|>': u"sc_arc_access", + u'_-|>': u"sc_arc_access", + u'<|-': u"sc_arc_access", + u'_<|-': u"sc_arc_access", + u'-/>': u"sc_arc_access", + u'_-/>': u"sc_arc_access", + u'': u"sc_arc_access", + u'_~>': u"sc_arc_access", + u'<~': u"sc_arc_access", + u'_<~': u"sc_arc_access", + u'~|>': u"sc_arc_access", + u'_~|>': u"sc_arc_access", + u'<|~': u"sc_arc_access", + u'_<|~': u"sc_arc_access", + u'~/>': u"sc_arc_access", + u'_~/>': u"sc_arc_access", + u'': u"sc_edge_const", - u'_<=>': u"sc_edge_var", - u'=>': u"sc_arc_common_const", - u'<=': u"sc_arc_common_const", - u'_=>': u"sc_arc_common_var", - u'_<=': u"sc_arc_common_var", - u'_->': u"sc_arc_access_var_pos_perm", - u'_<-': u"sc_arc_access_var_pos_perm", - u'-|>': u"sc_arc_access_const_neg_perm", - u'_-|>': u"sc_arc_access_var_neg_perm", - u'<|-': u"sc_arc_access_const_neg_perm", - u'_<|-': u"sc_arc_access_var_neg_perm", - u'-/>': u"sc_arc_access_const_fuz_perm", - u'_-/>': u"sc_arc_access_var_fuz_perm", - u'': u"sc_arc_access_const_pos_temp", - u'_~>': u"sc_arc_access_var_pos_temp", - u'<~': u"sc_arc_access_const_pos_temp", - u'_<~': u"sc_arc_access_var_pos_temp", - u'~|>': u"sc_arc_access_const_neg_temp", - u'_~|>': u"sc_arc_access_var_neg_temp", - u'<|~': u"sc_arc_access_const_neg_temp", - u'_<|~': u"sc_arc_access_var_neg_temp", - u'~/>': u"sc_arc_access_const_fuz_temp", - u'_~/>': u"sc_arc_access_var_fuz_temp", - u'': u"sc_edge_const", + u'_<=>': u"sc_edge_var", + u'=>': u"sc_arc_common_const", + u'<=': u"sc_arc_common_const", + u'_=>': u"sc_arc_common_var", + u'_<=': u"sc_arc_common_var", + u'_->': u"sc_arc_access_var_pos_perm", + u'_<-': u"sc_arc_access_var_pos_perm", + u'-|>': u"sc_arc_access_const_neg_perm", + u'_-|>': u"sc_arc_access_var_neg_perm", + u'<|-': u"sc_arc_access_const_neg_perm", + u'_<|-': u"sc_arc_access_var_neg_perm", + u'-/>': u"sc_arc_access_const_fuz_perm", + u'_-/>': u"sc_arc_access_var_fuz_perm", + u'': u"sc_arc_access_const_pos_temp", + u'_~>': u"sc_arc_access_var_pos_temp", + u'<~': u"sc_arc_access_const_pos_temp", + u'_<~': u"sc_arc_access_var_pos_temp", + u'~|>': u"sc_arc_access_const_neg_temp", + u'_~|>': u"sc_arc_access_var_neg_temp", + u'<|~': u"sc_arc_access_const_neg_temp", + u'_<|~': u"sc_arc_access_var_neg_temp", + u'~/>': u"sc_arc_access_const_fuz_temp", + u'_~/>': u"sc_arc_access_var_fuz_temp", + u'', False), res, False) - - return res - - def resolve_identifier(self, group): - """Resolves identifiers for different groups - """ - - key = str(group) - if self.aliases.has_key(key): - return self.aliases[key] - - alias = str(group) - if isinstance(group, scs_parser.OSetGroup): - alias = self.generate_oset_idtf() - elif isinstance(group, scs_parser.SetGroup): - alias = self.generate_set_idtf() - - self.aliases[key] = alias - - return alias - - def append_synonyms(self, idtf1, idtf2): - """Appends two identifiers as synonyms into map - """ - if self.synonyms.has_key(idtf1): - return - - self.synonyms[idtf1] = idtf2 - - def resolve_synonym(self, idtf): - if self.synonyms.has_key(idtf): - return self.resolve_synonym(self.synonyms[idtf]) - - return idtf - - def check_predicate_mirror(self, predicate): - - return (predicate in mirror_connectors) - - def append_sentence(self, subject, predicate, object, isMirrored): - """Appends new scs-level 1 sentence into list - """ - assert isinstance(subject, str) or isinstance(subject, unicode) - assert isinstance(object, str) or isinstance(object, unicode) - assert isinstance(predicate, str) or isinstance(predicate, unicode) - - if not isMirrored: - self.triples.append((subject, predicate, object)) - else: - self.triples.append((object, predicate, subject)) - - # --------------------------------------- - def processSimpleIdentifierGroup(self, group): - self.resolve_identifier(group) - - def processUrlGroup(self, group): - """Process url to link content data - """ - data_path = group.value[1:-1] - if data_path.startswith(u"file://"): - data_path = data_path.replace(u"file://", u"") - - path, tail = os.path.split(self.process_file) - abs_path = os.path.join(path, data_path) - if self.contents_copy_link.has_key(abs_path): - group.value = '"file://%s"' % self.contents_copy_link[abs_path] - else: - link_idtf = self.generate_link_idtf() - self.link_copy_contents[link_idtf] = abs_path - self.contents_copy_link[abs_path] = link_idtf - group.value = '"file://%s"' % link_idtf - - def processKeywordGroup(self, group): - return group - - def processSimpleSentenceGroup(self, group): - """Process scs-level 1 sentences - """ - subject_idtf = self.resolve_identifier(group.subject) - object_idtf = self.resolve_identifier(group.object) - arc_idtf = group.predicate.value - - self.append_sentence(subject_idtf, arc_idtf, object_idtf, False); - - def processIdtfWithIntGroup(self, group): - """Process identifier with internal sentence group - """ - self.parse_tree(group.idtf) - subject_idtf = self.resolve_identifier(group.idtf) - - internal_list = group.internal - if internal_list is not None: - for sentence in internal_list.sentences: - - for obj in sentence.object: - self.parse_tree(obj) - object_idtf = self.resolve_identifier(obj) - attributes = sentence.attrs - arc_idtf = self.generate_arc_idtf(sentence.predicate) - - self.append_sentence(subject_idtf, arc_idtf, object_idtf, self.check_predicate_mirror(sentence.predicate)) - - # write attributes - for attr in attributes: - attr_idtf = self.resolve_identifier(attr) - self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) - - - def processInternalGroup(self, group): - return group - - def processInternalListGroup(self, group): - return group - - def processTripleGroup(self, group): - return group - - def processAliasGroup(self, group): - """Just resolve identifier for alias - """ - return self.resolve_identifier(group) - - def processContentGroup(self, group): - """Store link content for saving - """ - result = None - if len(group.value) > 1 and group.value[0] == u'*' and group.value[-1] == u'*': - data = group.value[1:-1] - data_str = data - process_file = self.process_file - process_dir = self.process_dir - - if data.startswith(u'^'): - data = data[2:-1] - if data.startswith(u"file://"): - data = data.replace(u"file://", u"") - - process_dir, tail = os.path.split(self.process_file) - process_file = os.path.join(process_dir, data) - f = open(process_file, "r") - data_str = f.read() - f.close() - - - - # create new converter and build data - converter = Converter() - converter.link_contents = self.link_contents - converter.synonyms = self.synonyms - #converter.triples = self.triples - converter.aliases = self.aliases - converter.set_count = self.set_count - converter.oset_count = self.oset_count - converter.arc_count = self.arc_count - converter.link_count = self.link_count - converter.process_file = self.process_file - converter.process_dir = self.process_dir - - converter.parse_string(data_str) - - self.link_contents = converter.link_contents - self.synonyms = converter.synonyms - self.triples.extend(converter.triples) - self.aliases = converter.aliases - self.set_count = converter.set_count - self.oset_count = converter.oset_count - self.arc_count = converter.arc_count - self.link_count = converter.link_count - - # collect all created sc-elements to append them into contour - objects = [] - for triple in converter.triples: - if triple[0] in arc_types.values() or triple[0] in arc_keynodes.values(): - continue - for idx in xrange(len(triple)): - if not triple[idx] in objects: - objects.append(triple[idx]) - - contour = self.generate_contour_idtf() - for obj in objects: - self.append_sentence(contour, self.generate_arc_idtf('->', False), obj, False) - - group.value = contour - result = contour - else: - link_idtf = self.generate_link_idtf() - self.link_contents[link_idtf] = group.value - group.value = '"file://%s"' % link_idtf - result = link_idtf - - return result - - def processSetGroup(self, group): - """Process set - """ - idtf = self.resolve_identifier(group) - for item in group.items: - attributes = item[0] - object = item[1] - - self.parse_tree(object) - arc_idtf = self.generate_arc_idtf('->') - self.append_sentence(idtf, arc_idtf, self.resolve_identifier(object), False) - - for attr in attributes: - attr_idtf = self.resolve_identifier(attr) - self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) - - - def processOSetGroup(self, group): - """Process ordered tree - """ - idtf = self.resolve_identifier(group) - item_count = 0 - for item in group.items: - attributes = item[0] - object = item[1] - item_count += 1 - - object_idtf = self.resolve_identifier(object) - self.parse_tree(object) - arc_idtf = self.generate_arc_idtf('->') - self.append_sentence(idtf, arc_idtf, object_idtf, False) - # add order attribute - self.append_sentence("%d_" % item_count, self.generate_arc_idtf('->'), arc_idtf, False) - - for attr in attributes: - attr_idtf = self.resolve_identifier(attr) - self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), object_idtf, False) - - def processSentenceGroup(self, group): - """Process sentence for scs-levels 2-6 - """ - subject = group.subject - predicate = group.predicate - attributes = group.attrs - objects = group.object - - self.parse_tree(subject) - subject_idtf = self.resolve_identifier(subject) - for obj in objects: - # process object - self.parse_tree(obj) - # resolve object identifier - obj_idtf = self.resolve_identifier(obj) - - - if predicate == u'=': - self.append_synonyms(obj_idtf, subject_idtf) - else: - # connect subject with object - arc_idtf = self.generate_arc_idtf(predicate) - self.append_sentence(subject_idtf, arc_idtf, obj_idtf, self.check_predicate_mirror(predicate)) - - # connect attributes - for attr in attributes: - self.parse_tree(attr) - attr_idtf = self.resolve_identifier(attr) - self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) - - def processSynonymGroup(self, group): - pass - - - def get_string_value(self, element): - """Returns string representation of specified element - from parser results - """ - pass - - def parse_skip(self, group): - pass - - def parse_simple_sentence(self, group): - pass - - - # ----------------------------------------- - def parse_tree(self, tree): - """Parse results tree - """ - - if isinstance(tree, ParseResults): - for item in tree: - self.parse_tree(item) - else: - self.group_processors[tree.__class__](tree) - - - def parse_string(self, data): - """Parse specified string - """ - try: - result = scs_parser.syntax().parseString(data.decode('utf-8'), parseAll = True) - self.parse_tree(result) - - # process synonyms - new_triples = [] - for subj, predicate, obj in self.triples: - new_triples.append((self.resolve_synonym(subj), predicate, self.resolve_synonym(obj))) - self.triples = new_triples - - - except ParseException, err: - print err.line - print " "*(err.column-1) + "^" - print err - - def parse_directory(self, path): - """Parse specified directory - """ - self.process_dir = path - for root, dirs, files in os.walk(path): - #print root - for f in files: - - # skip none scs files - base, ext = os.path.splitext(f) - if ext != '.scs': - continue - - file_path = os.path.join(root, f) - self.process_file = file_path - - self.comments[len(self.triples)] = file_path - - # parse file - print "Parse %s" % file_path - input = open(file_path, "r") - self.parse_string(input.read().decode("utf-8")) - input.close() - - def write_to_fs(self, path): - """Writes converted data into specified directory - """ - if os.path.exists(path): - shutil.rmtree(path) - - os.makedirs(path) - - count = 0 - with codecs.open(os.path.join(path, "data.scs"), "w", "utf-8") as output: - for t in self.triples: - - if self.comments.has_key(count): - output.write('\n/* --- %s --- */\n' % self.comments[count]) - - output.write("%s | %s | %s;;\n" % t) - count += 1 - output.close() - - # write contents - os.makedirs(os.path.join(path, "data")) - for num, data in self.link_contents.items(): - f = open(os.path.join(path, str(num)), "w") - f.write(data) - f.close() - - # copy contents - for num, src_path in self.link_copy_contents.items(): - shutil.copyfile(src_path, os.path.join(path, str(num))) + + def __init__(self): + self.group_processors = { + scs_parser.AliasGroup: self.processAliasGroup, + scs_parser.KeywordGroup: self.processKeywordGroup, + scs_parser.SimpleIdentifierGroup: self.processSimpleIdentifierGroup, + scs_parser.UrlGroup: self.processUrlGroup, + scs_parser.ContentGroup: self.processContentGroup, + scs_parser.SetGroup: self.processSetGroup, + scs_parser.OSetGroup: self.processOSetGroup, + scs_parser.TripleGroup: self.processTripleGroup, + scs_parser.SimpleSentenceGroup: self.processSimpleSentenceGroup, + #scs_parser.SynonymGroup: self.processSynonymGroup, + scs_parser.SentenceGroup: self.processSentenceGroup, + scs_parser.IdtfWithIntGroup: self.processIdtfWithIntGroup, + scs_parser.InternalGroup: self.processInternalGroup, + scs_parser.InternalListGroup: self.processInternalListGroup + } + + self.comments = {} + self.triples = [] + + # map of contents, that need to be written + self.link_contents = {} + # map of contents, that need to be copied + self.link_copy_contents = {} + self.contents_copy_link = {} + + # synonyms map. For each key in this map we contains list of all synonym elements + self.synonyms = {} + # aliases map + self.aliases = {} + + self.set_count = 0 + self.contour_count = 0 + self.oset_count = 0 + self.arc_count = 0 + self.link_count = 0 + + self.process_file = None + self.process_dir = None + + def generate_set_idtf(self): + self.set_count += 1 + return ".set_%d" % self.set_count + + def generate_contour_idtf(self): + self.contour_count += 1 + return ".contour_%d" % self.contour_count + + def generate_oset_idtf(self): + self.oset_count += 1 + return ".oset_%d" % self.oset_count + + def generate_link_idtf(self): + self.link_count += 1 + return 'data/link_%d' % self.link_count + + def generate_arc_idtf(self, type = None, include_into_set = True): + self.arc_count += 1 + + res = None + if type is not None: + if arc_types.has_key(type): + res = arc_types[type] + "#." + str(self.arc_count) + + if res is None: + res = "sc_arc_common#.arc_%d" % self.arc_count + + if include_into_set and arc_keynodes.has_key(type): + self.append_sentence(arc_keynodes[type], self.generate_arc_idtf('->', False), res, False) + + return res + + def resolve_identifier(self, group): + """Resolves identifiers for different groups + """ + + key = str(group) + if self.aliases.has_key(key): + return self.aliases[key] + + alias = str(group) + if isinstance(group, scs_parser.OSetGroup): + alias = self.generate_oset_idtf() + elif isinstance(group, scs_parser.SetGroup): + alias = self.generate_set_idtf() + + self.aliases[key] = alias + + return alias + + def append_synonyms(self, idtf1, idtf2): + """Appends two identifiers as synonyms into map + """ + if self.synonyms.has_key(idtf1): + return + + self.synonyms[idtf1] = idtf2 + + def resolve_synonym(self, idtf): + if self.synonyms.has_key(idtf): + return self.resolve_synonym(self.synonyms[idtf]) + + return idtf + + def check_predicate_mirror(self, predicate): + + return (predicate in mirror_connectors) + + def append_sentence(self, subject, predicate, object, isMirrored): + """Appends new scs-level 1 sentence into list + """ + assert isinstance(subject, str) or isinstance(subject, unicode) + assert isinstance(object, str) or isinstance(object, unicode) + assert isinstance(predicate, str) or isinstance(predicate, unicode) + + if not isMirrored: + self.triples.append((subject, predicate, object)) + else: + self.triples.append((object, predicate, subject)) + + # --------------------------------------- + def processSimpleIdentifierGroup(self, group): + self.resolve_identifier(group) + + def processUrlGroup(self, group): + """Process url to link content data + """ + data_path = group.value[1:-1] + if data_path.startswith(u"file://"): + data_path = data_path.replace(u"file://", u"") + + path, tail = os.path.split(self.process_file) + abs_path = os.path.abspath(os.path.join(path, data_path)) + if self.contents_copy_link.has_key(abs_path): + group.value = '"file://%s"' % self.contents_copy_link[abs_path] + else: + link_idtf = self.generate_link_idtf() + self.link_copy_contents[link_idtf] = abs_path + self.contents_copy_link[abs_path] = link_idtf + group.value = '"file://%s"' % link_idtf + + def processKeywordGroup(self, group): + return group + + def processSimpleSentenceGroup(self, group): + """Process scs-level 1 sentences + """ + subject_idtf = self.resolve_identifier(group.subject) + object_idtf = self.resolve_identifier(group.object) + arc_idtf = group.predicate.value + + self.append_sentence(subject_idtf, arc_idtf, object_idtf, False); + + def processIdtfWithIntGroup(self, group): + """Process identifier with internal sentence group + """ + self.parse_tree(group.idtf) + subject_idtf = self.resolve_identifier(group.idtf) + + internal_list = group.internal + if internal_list is not None: + for sentence in internal_list.sentences: + + for obj in sentence.object: + self.parse_tree(obj) + object_idtf = self.resolve_identifier(obj) + attributes = sentence.attrs + arc_idtf = self.generate_arc_idtf(sentence.predicate) + + self.append_sentence(subject_idtf, arc_idtf, object_idtf, self.check_predicate_mirror(sentence.predicate)) + + # write attributes + for attr in attributes: + attr_idtf = self.resolve_identifier(attr) + self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) + + + def processInternalGroup(self, group): + return group + + def processInternalListGroup(self, group): + return group + + def processTripleGroup(self, group): + return group + + def processAliasGroup(self, group): + """Just resolve identifier for alias + """ + return self.resolve_identifier(group) + + def processContentGroup(self, group): + """Store link content for saving + """ + result = None + if len(group.value) > 1 and group.value[0] == u'*' and group.value[-1] == u'*': + data = group.value[1:-1] + data_str = data + process_file = self.process_file + process_dir = self.process_dir + + if data.startswith(u'^'): + data = data[2:-1] + if data.startswith(u"file://"): + data = data.replace(u"file://", u"") + + process_dir, tail = os.path.split(self.process_file) + process_file = os.path.join(process_dir, data) + f = open(process_file, "r") + data_str = f.read() + f.close() + + + + # create new converter and build data + converter = Converter() + converter.link_copy_contents = self.link_copy_contents + converter.link_contents = self.link_contents + converter.synonyms = self.synonyms + #converter.triples = self.triples + converter.aliases = self.aliases + converter.set_count = self.set_count + converter.oset_count = self.oset_count + converter.arc_count = self.arc_count + converter.link_count = self.link_count + converter.process_file = self.process_file + converter.process_dir = self.process_dir + + converter.parse_string(data_str) + + self.link_copy_contents = converter.link_copy_contents + self.link_contents = converter.link_contents + self.synonyms = converter.synonyms + self.triples.extend(converter.triples) + self.aliases = converter.aliases + self.set_count = converter.set_count + self.oset_count = converter.oset_count + self.arc_count = converter.arc_count + self.link_count = converter.link_count + + # collect all created sc-elements to append them into contour + objects = [] + for triple in converter.triples: + if triple[0] in arc_types.values() or triple[0] in arc_keynodes.values(): + continue + for idx in xrange(len(triple)): + if not triple[idx] in objects: + objects.append(triple[idx]) + + contour = self.generate_contour_idtf() + for obj in objects: + self.append_sentence(contour, self.generate_arc_idtf('->', False), obj, False) + + group.value = contour + result = contour + else: + link_idtf = self.generate_link_idtf() + self.link_contents[link_idtf] = group.value + group.value = '"file://%s"' % link_idtf + result = link_idtf + + return result + + def processSetGroup(self, group): + """Process set + """ + idtf = self.resolve_identifier(group) + for item in group.items: + attributes = item[0] + object = item[1] + + self.parse_tree(object) + arc_idtf = self.generate_arc_idtf('->') + self.append_sentence(idtf, arc_idtf, self.resolve_identifier(object), False) + + for attr in attributes: + attr_idtf = self.resolve_identifier(attr) + self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) + + + def processOSetGroup(self, group): + """Process ordered tree + """ + idtf = self.resolve_identifier(group) + item_count = 0 + for item in group.items: + attributes = item[0] + object = item[1] + item_count += 1 + + object_idtf = self.resolve_identifier(object) + self.parse_tree(object) + arc_idtf = self.generate_arc_idtf('->') + self.append_sentence(idtf, arc_idtf, object_idtf, False) + # add order attribute + self.append_sentence("%d_" % item_count, self.generate_arc_idtf('->'), arc_idtf, False) + + for attr in attributes: + attr_idtf = self.resolve_identifier(attr) + self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), object_idtf, False) + + def processSentenceGroup(self, group): + """Process sentence for scs-levels 2-6 + """ + subject = group.subject + predicate = group.predicate + attributes = group.attrs + objects = group.object + + self.parse_tree(subject) + subject_idtf = self.resolve_identifier(subject) + for obj in objects: + # process object + self.parse_tree(obj) + # resolve object identifier + obj_idtf = self.resolve_identifier(obj) + + + if predicate == u'=': + self.append_synonyms(obj_idtf, subject_idtf) + else: + # connect subject with object + arc_idtf = self.generate_arc_idtf(predicate) + self.append_sentence(subject_idtf, arc_idtf, obj_idtf, self.check_predicate_mirror(predicate)) + + # connect attributes + for attr in attributes: + self.parse_tree(attr) + attr_idtf = self.resolve_identifier(attr) + self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) + + def processSynonymGroup(self, group): + pass + + + def get_string_value(self, element): + """Returns string representation of specified element + from parser results + """ + pass + + def parse_skip(self, group): + pass + + def parse_simple_sentence(self, group): + pass + + + # ----------------------------------------- + def parse_tree(self, tree): + """Parse results tree + """ + + if isinstance(tree, ParseResults): + for item in tree: + self.parse_tree(item) + else: + self.group_processors[tree.__class__](tree) + + + def parse_string(self, data): + """Parse specified string + """ + try: + result = scs_parser.syntax().parseString(data.decode('utf-8'), parseAll = True) + self.parse_tree(result) + + # process synonyms + new_triples = [] + for subj, predicate, obj in self.triples: + new_triples.append((self.resolve_synonym(subj), predicate, self.resolve_synonym(obj))) + self.triples = new_triples + + + except ParseException, err: + print err.line + print " "*(err.column-1) + "^" + print err + + def parse_directory(self, path): + """Parse specified directory + """ + self.process_dir = path + for root, dirs, files in os.walk(path): + #print root + for f in files: + + # skip none scs files + base, ext = os.path.splitext(f) + if ext != '.scs': + continue + + file_path = os.path.join(root, f) + self.process_file = file_path + + self.comments[len(self.triples)] = file_path + + # parse file + print "Parse %s" % file_path + input = open(file_path, "r") + self.parse_string(input.read().decode("utf-8")) + input.close() + + def write_to_fs(self, path): + """Writes converted data into specified directory + """ + if os.path.exists(path): + shutil.rmtree(path) + + os.makedirs(path) + + count = 0 + with codecs.open(os.path.join(path, "data.scs"), "w", "utf-8") as output: + for t in self.triples: + + if self.comments.has_key(count): + output.write('\n/* --- %s --- */\n' % self.comments[count]) + + output.write("%s | %s | %s;;\n" % t) + count += 1 + output.close() + + # write contents + os.makedirs(os.path.join(path, "data")) + for num, data in self.link_contents.items(): + f = open(os.path.join(path, str(num)), "w") + f.write(data) + f.close() + + # copy contents + for num, src_path in self.link_copy_contents.items(): + shutil.copyfile(src_path, os.path.join(path, str(num))) if __name__ == "__main__": - print "Default encoding: %s" % sys.getdefaultencoding() + print "Default encoding: %s" % sys.getdefaultencoding() - if len(sys.argv) < 3: - print "Usage: python converter.py " - sys.exit(0) - - converter = Converter() - converter.parse_directory(sys.argv[1]) - print "Write output..." - converter.write_to_fs(sys.argv[2]) + if len(sys.argv) < 3: + print "Usage: python converter.py " + sys.exit(0) + + converter = Converter() + converter.parse_directory(sys.argv[1]) + print "Write output..." + converter.write_to_fs(sys.argv[2]) From c0e1c1ef9657c68737bbe9cb57037694ca643ab7 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Sat, 9 Feb 2013 01:50:36 +0300 Subject: [PATCH 23/84] rename nrel_system_identifier to hypermedia_nrel_system_identifier --- tools/scs/builder.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index 7664fc27..d13d6565 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -208,8 +208,6 @@ def createNodeOrLink(elIdtf, elType): #print path, addr.seg, addr.offset stream = None - if path == u'data/link_75': - pass if conv.link_copy_contents.has_key(path): cont_path = str(conv.link_copy_contents[path]) if os.path.exists(cont_path): @@ -283,7 +281,7 @@ def generateIdentifiers(): global nrel_idtf_addr # create 'nrel_system_identifier' keynode if it doesn't exist - nrel_idtf_str = u'nrel_system_identifier' + nrel_idtf_str = u'hypermedia_nrel_system_identifier' nrel_idtf_data = str(nrel_idtf_str) #sc_helper_init() From 23fd228e4e4cb872ee6062af876c7aac5cd0e266 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Sun, 10 Feb 2013 19:18:32 +0300 Subject: [PATCH 24/84] fixed bug with include path calculation --- tools/scs/converter.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/scs/converter.py b/tools/scs/converter.py index b1b70d40..17e7a3de 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -341,6 +341,9 @@ def processContentGroup(self, group): data = data[2:-1] if data.startswith(u"file://"): data = data.replace(u"file://", u"") + + if data.endswith(u'_content.scsi'): + pass process_dir, tail = os.path.split(self.process_file) process_file = os.path.join(process_dir, data) @@ -361,8 +364,8 @@ def processContentGroup(self, group): converter.oset_count = self.oset_count converter.arc_count = self.arc_count converter.link_count = self.link_count - converter.process_file = self.process_file - converter.process_dir = self.process_dir + converter.process_file = process_file + converter.process_dir = process_dir converter.parse_string(data_str) From da3f88b612089ae6d310fa7f6b3cde9f710d4f7a Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Mon, 11 Feb 2013 01:59:54 +0300 Subject: [PATCH 25/84] add translation of sc-link data formats --- tools/scs/builder.py | 6 +++--- tools/scs/converter.py | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index d13d6565..c17e9d14 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -225,6 +225,7 @@ def createNodeOrLink(elIdtf, elType): if res != SC_RESULT_OK: print "Can't setup link data for %s" % (str(elIdtf)) sc_stream_free(stream) + elif elType & sc_type_node: @@ -303,12 +304,11 @@ def generateIdentifiers(): if system_idtf.startswith('.') or (system_idtf.startswith('"') and system_idtf.endswith('"')) or system_idtf.startswith('_.'): continue - # temporary hack if system_idtf == nrel_idtf_str: continue - #print "\tSetup for %s" % idtf + #print "\tSetup for %s, %d: %d" % (idtf, addr.seg, addr.offset) assert addr is not None # generate identifier relation @@ -376,7 +376,7 @@ def generateIdentifiers(): arc.begin = subject arc.end = object arc.type = sc_types[predicate] - + sc_arcs[predicate] = arc # now create arcs, while there are any arcs not created, diff --git a/tools/scs/converter.py b/tools/scs/converter.py index 17e7a3de..d422497c 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -234,6 +234,16 @@ def append_synonyms(self, idtf1, idtf2): self.synonyms[idtf1] = idtf2 + def buildFormatRelation(self, linkIdtf, ext): + """Build relation between sc-link and it's format + @param linkIdtf: Identifier of sc-link + @param ext: File extension + """ + fmt_idtf = u'format_' + ext + arc_idtf = self.generate_arc_idtf(u'=>', True) + self.append_sentence(linkIdtf, arc_idtf, fmt_idtf, False) + self.append_sentence(u'hypermedia_nrel_format', self.generate_arc_idtf(u'->', True), arc_idtf, False) + def resolve_synonym(self, idtf): if self.synonyms.has_key(idtf): return self.resolve_synonym(self.synonyms[idtf]) @@ -276,6 +286,9 @@ def processUrlGroup(self, group): self.link_copy_contents[link_idtf] = abs_path self.contents_copy_link[abs_path] = link_idtf group.value = '"file://%s"' % link_idtf + + path, ext = os.path.splitext(abs_path) + self.buildFormatRelation(link_idtf, ext[1:]) def processKeywordGroup(self, group): return group @@ -342,8 +355,6 @@ def processContentGroup(self, group): if data.startswith(u"file://"): data = data.replace(u"file://", u"") - if data.endswith(u'_content.scsi'): - pass process_dir, tail = os.path.split(self.process_file) process_file = os.path.join(process_dir, data) @@ -399,6 +410,8 @@ def processContentGroup(self, group): self.link_contents[link_idtf] = group.value group.value = '"file://%s"' % link_idtf result = link_idtf + + self.buildFormatRelation(group.value, u'txt') return result From 07dd4cc982c517ed268d4b987f79f443f39d0074 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Sat, 16 Feb 2013 15:16:16 +0300 Subject: [PATCH 26/84] fixed keynode identifiers; fixed bug with file data translation --- tools/scs/builder.py | 3 +++ tools/scs/converter.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index c17e9d14..eea7d60c 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -236,6 +236,8 @@ def createNodeOrLink(elIdtf, elType): else: raise "Unknown type" + if elIdtf.startswith(u'data'): + pass sc_addrs[elIdtf] = addr def resolveScAddr(idtf): @@ -309,6 +311,7 @@ def generateIdentifiers(): continue #print "\tSetup for %s, %d: %d" % (idtf, addr.seg, addr.offset) + #print system_idtf assert addr is not None # generate identifier relation diff --git a/tools/scs/converter.py b/tools/scs/converter.py index d422497c..ec48a945 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -239,7 +239,7 @@ def buildFormatRelation(self, linkIdtf, ext): @param linkIdtf: Identifier of sc-link @param ext: File extension """ - fmt_idtf = u'format_' + ext + fmt_idtf = u'hypermedia_format_' + ext arc_idtf = self.generate_arc_idtf(u'=>', True) self.append_sentence(linkIdtf, arc_idtf, fmt_idtf, False) self.append_sentence(u'hypermedia_nrel_format', self.generate_arc_idtf(u'->', True), arc_idtf, False) @@ -288,7 +288,7 @@ def processUrlGroup(self, group): group.value = '"file://%s"' % link_idtf path, ext = os.path.splitext(abs_path) - self.buildFormatRelation(link_idtf, ext[1:]) + self.buildFormatRelation(group.value, ext[1:]) def processKeywordGroup(self, group): return group From 35f3cb0c1fcd20ad4270ede8db0b472cc52d3776 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Sun, 17 Feb 2013 01:30:14 +0300 Subject: [PATCH 27/84] fixed bug with format relation, for system identifiers --- tools/scs/builder.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index eea7d60c..1650d69a 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -28,6 +28,11 @@ from pysc import * from pysc import _sc_addr +# keynode identifires +keynode_hypermedia_format_txt_str = u"hypermedia_format_txt" +keynode_hypermedia_nrel_format_str = u"hypermedia_nrel_format" + + encoding = "utf-8" reload(sys) sys.setdefaultencoding(encoding) @@ -65,7 +70,13 @@ # nodes u"sc_const": sc_type_const, u"sc_var": sc_type_var, - u"sc_node_norole_relation": sc_type_node | sc_type_node_norole, + u"sc_node_not_binary_tuple": sc_type_node_tuple, + u"sc_node_struct": sc_type_node_struct, + u"sc_node_role_relation": sc_type_node_role, + u"sc_node_norole_relation": sc_type_node_norole, + u"sc_node_not_relation": sc_type_node_class, + u"sc_node_abstract": sc_type_node_abstract, + u"sc_node_material": sc_type_node_material, } # mapping identifiers into sc-addrs @@ -235,9 +246,7 @@ def createNodeOrLink(elIdtf, elType): else: raise "Unknown type" - - if elIdtf.startswith(u'data'): - pass + sc_addrs[elIdtf] = addr def resolveScAddr(idtf): @@ -259,6 +268,9 @@ def generateSystemIdentifier(el_addr, idtf): global created_arcs global created_links + format_txt = sc_addrs[keynode_hypermedia_format_txt_str] + nrel_format = sc_addrs[keynode_hypermedia_nrel_format_str] + idtf_data = str(idtf) stream = sc_stream_memory_new(idtf_data, len(idtf_data), SC_STREAM_READ, False) @@ -276,6 +288,10 @@ def generateSystemIdentifier(el_addr, idtf): arc_addr = sc_memory_arc_new(sc_type_arc_common | sc_type_const, el_addr, idtf_link) sc_memory_arc_new(sc_type_arc_pos_const_perm, nrel_idtf_addr, arc_addr) + # setup format + arc_addr = sc_memory_arc_new(sc_type_arc_common | sc_type_const, idtf_link, format_txt) + sc_memory_arc_new(sc_type_arc_pos_const_perm, nrel_format, arc_addr) + created_links += 1 created_arcs += 2 From b655258e6ac2248af03bd39882f242af7bb1a634 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Wed, 20 Feb 2013 12:32:29 +0300 Subject: [PATCH 28/84] some code rafactored; fixed bug with struct types for nodes --- tools/scs/builder.py | 652 +++++++++++++++++++++-------------------- tools/scs/converter.py | 3 +- 2 files changed, 332 insertions(+), 323 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index 1650d69a..40f7c3d4 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -79,22 +79,6 @@ u"sc_node_material": sc_type_node_material, } -# mapping identifiers into sc-addrs -sc_addrs = {} -# map of sc-element types -sc_types = {} -# map of sc-arc, that need to be created -sc_arcs = {} - -# statistics -created_nodes = 0 -created_links = 0 -created_arcs = 0 - -nrel_idtf_addr = None - -input_path = None -output_path = None # -------------------------------------------------- @@ -120,343 +104,367 @@ def __init__(self): # -------------------------------------------------- -def checkIdtfWithPrefix(idtf): - """Check if specified identifier contains type - """ - return idtf.count(u'#') > 0 - -def checkIdtfIsLink(idtf): - """Checks if specified idtf is and sc-link - """ - return len(idtf) > 1 and idtf[0] == u'"' and idtf[-1] == u'"' - -def checkIdtfIsArc(idtf): - """Check if specified identifier is an arc identifier. - This function analyze just identifier - """ - if checkIdtfWithPrefix(idtf): - preffix, el_idtf = splitIdtf(idtf) - if arcPrefixToType.has_key(preffix): - return True - - return False - -def splitIdtf(idtf): - """Split identifier with prefix to type and identifier - """ - assert checkIdtfWithPrefix(idtf) +class ScBuilder: - res = idtf.split('#') - return (res[0], res[1]) - + def __init__(self): + # mapping identifiers into sc-addrs + self.sc_addrs = {} + # map of sc-element types + self.sc_types = {} + # map of sc-arc, that need to be created + self.sc_arcs = {} + self.conv = None + + # statistics + self.created_nodes = 0 + self.created_links = 0 + self.created_arcs = 0 + + self.nrel_idtf_addr = None + + self.input_path = None + self.output_path = None -def determineArcType(arc_idtf): - """Determine type of predicate - """ - if checkIdtfWithPrefix(arc_idtf): - prefix, idtf = splitIdtf(arc_idtf) - return arcPrefixToType[prefix] + def checkIdtfWithPrefix(self, idtf): + """Check if specified identifier contains type + """ + return idtf.count(u'#') > 0 + + def checkIdtfIsLink(self, idtf): + """Checks if specified idtf is and sc-link + """ + return len(idtf) > 1 and idtf[0] == u'"' and idtf[-1] == u'"' + + def checkIdtfIsArc(self, idtf): + """Check if specified identifier is an arc identifier. + This function analyze just identifier + """ + if self.checkIdtfWithPrefix(idtf): + preffix, el_idtf = self.splitIdtf(idtf) + if arcPrefixToType.has_key(preffix): + return True + + return False - return sc_type_arc_common - -def determineElementType(idtf, isPredicate): - """Determines element type and store it in types map - """ - oldType = None - try: - oldType = sc_types[idtf] - except: - pass + def splitIdtf(self, idtf): + """Split identifier with prefix to type and identifier + """ + assert self.checkIdtfWithPrefix(idtf) + + res = idtf.split('#') + return (res[0], res[1]) - isVariable = idtf.startswith(u"_") - newType = None - if checkIdtfIsLink(idtf): - newType = sc_type_link - else: - if checkIdtfIsArc(idtf): - newType = determineArcType(idtf) + def determineArcType(self, arc_idtf): + """Determine type of predicate + """ + if self.checkIdtfWithPrefix(arc_idtf): + prefix, idtf = self.splitIdtf(arc_idtf) + return arcPrefixToType[prefix] + + return sc_type_arc_common - if newType is None: - if isPredicate: - newType = sc_type_arc_common + def determineElementType(self, idtf, isPredicate): + """Determines element type and store it in types map + """ + oldType = None + try: + oldType = self.sc_types[idtf] + except: + pass + + isVariable = idtf.startswith(u"_") + + newType = None + if self.checkIdtfIsLink(idtf): + newType = sc_type_link else: - newType = sc_type_node - - if isVariable: - newType = newType | sc_type_var - else: - newType = newType | sc_type_const - - if oldType is not None: - # determine if new element type more common then old type - if newType == (newType & oldType): - sc_types[idtf] = newType - else: - sc_types[idtf] = newType - -def resolveLinkPath(idtf): - """Resolve sc-link file path - """ - path = idtf[1:-1] - path = path.replace("file://", "") - - return os.path.join(input_path, path) - - -def createNodeOrLink(elIdtf, elType): - """Create sc-node or sc-link in memory - """ - if elType & sc_type_link: - addr = sc_memory_link_new() - global created_links - created_links += 1 - - - # setup link data - path = elIdtf[1:-1] - path = unicode(path.replace("file://", "")) - - #print path, addr.seg, addr.offset - stream = None - if conv.link_copy_contents.has_key(path): - cont_path = str(conv.link_copy_contents[path]) - if os.path.exists(cont_path): - stream = sc_stream_file_new(cont_path, SC_STREAM_READ) + if self.checkIdtfIsArc(idtf): + newType = self.determineArcType(idtf) + + if newType is None: + if isPredicate: + newType = sc_type_arc_common else: - print "File doesn't exist '%s'" % cont_path - elif conv.link_contents.has_key(path): - data = str(conv.link_contents[path]) - stream = sc_stream_memory_new(data, len(data), SC_STREAM_READ, False) + newType = sc_type_node - if stream is None: - print "Can't setup content from path %s" % path + if isVariable: + newType = newType | sc_type_var else: - res = sc_memory_set_link_content(addr, stream) - if res != SC_RESULT_OK: - print "Can't setup link data for %s" % (str(elIdtf)) - sc_stream_free(stream) + newType = newType | sc_type_const - - elif elType & sc_type_node: + if oldType is not None: + # determine if new element type more common then old type + if oldType == (newType & oldType): + self.sc_types[idtf] = newType + else: + self.sc_types[idtf] = newType + + def resolveLinkPath(self, idtf): + """Resolve sc-link file path + """ + path = idtf[1:-1] + path = path.replace("file://", "") - addr = sc_memory_node_new(elType) - global created_nodes - created_nodes += 1 + return os.path.join(input_path, path) - else: - raise "Unknown type" - - sc_addrs[elIdtf] = addr - -def resolveScAddr(idtf): - """Resolve sc-addr of element if it possible - """ - - if sc_addrs.has_key(idtf): - return - - obj_type = sc_types[idtf] - # create subject and object if them aren't an arcs - if not (obj_type & sc_type_arc_mask): - createNodeOrLink(idtf, obj_type) - -def generateSystemIdentifier(el_addr, idtf): - """Generate system identifier construction for specified node - """ - global created_nodes - global created_arcs - global created_links - - format_txt = sc_addrs[keynode_hypermedia_format_txt_str] - nrel_format = sc_addrs[keynode_hypermedia_nrel_format_str] - - idtf_data = str(idtf) - stream = sc_stream_memory_new(idtf_data, len(idtf_data), SC_STREAM_READ, False) - - assert stream is not None - - results_list = None - results_count = None - idtf_link = None - - idtf_link = sc_memory_link_new() - sc_memory_set_link_content(idtf_link, stream) - sc_stream_free(stream) - - # link nodes - arc_addr = sc_memory_arc_new(sc_type_arc_common | sc_type_const, el_addr, idtf_link) - sc_memory_arc_new(sc_type_arc_pos_const_perm, nrel_idtf_addr, arc_addr) - - # setup format - arc_addr = sc_memory_arc_new(sc_type_arc_common | sc_type_const, idtf_link, format_txt) - sc_memory_arc_new(sc_type_arc_pos_const_perm, nrel_format, arc_addr) - - created_links += 1 - created_arcs += 2 - -def generateIdentifiers(): - - global nrel_idtf_addr - - # create 'nrel_system_identifier' keynode if it doesn't exist - nrel_idtf_str = u'hypermedia_nrel_system_identifier' - nrel_idtf_data = str(nrel_idtf_str) - - #sc_helper_init() - - try: - nrel_idtf_addr = sc_addrs[nrel_idtf_str] - except: - raise "You need to define system identifier keynode in scs" - - assert nrel_idtf_addr is not None + + def createNodeOrLink(self, elIdtf, elType): + """Create sc-node or sc-link in memory + """ + if elType & sc_type_link: + addr = sc_memory_link_new() + self.created_links += 1 + + + # setup link data + path = elIdtf[1:-1] + path = unicode(path.replace("file://", "")) + + #print path, addr.seg, addr.offset + stream = None + if self.conv.link_copy_contents.has_key(path): + cont_path = str(self.conv.link_copy_contents[path]) + if os.path.exists(cont_path): + stream = sc_stream_file_new(cont_path, SC_STREAM_READ) + else: + print "File doesn't exist '%s'" % cont_path + elif self.conv.link_contents.has_key(path): + data = str(self.conv.link_contents[path]) + stream = sc_stream_memory_new(data, len(data), SC_STREAM_READ, False) + + if stream is None: + print "Can't setup content from path %s" % path + else: + res = sc_memory_set_link_content(addr, stream) + if res != SC_RESULT_OK: + print "Can't setup link data for %s" % (str(elIdtf)) + sc_stream_free(stream) + - for idtf, addr in sc_addrs.items(): + elif elType & sc_type_node: - # extract object identifier - system_idtf = idtf - if checkIdtfWithPrefix(system_idtf): - system_idtf = splitIdtf(system_idtf)[1] + addr = sc_memory_node_new(elType) + self.created_nodes += 1 - if system_idtf.startswith('.') or (system_idtf.startswith('"') and system_idtf.endswith('"')) or system_idtf.startswith('_.'): - continue + else: + raise "Unknown type" + + self.sc_addrs[elIdtf] = addr - # temporary hack - if system_idtf == nrel_idtf_str: - continue + def resolveScAddr(self, idtf): + """Resolve sc-addr of element if it possible + """ + if self.sc_addrs.has_key(idtf): + return - #print "\tSetup for %s, %d: %d" % (idtf, addr.seg, addr.offset) - #print system_idtf + obj_type = self.sc_types[idtf] + # create subject and object if them aren't an arcs + if not (obj_type & sc_type_arc_mask): + self.createNodeOrLink(idtf, obj_type) + + def generateSystemIdentifier(self, el_addr, idtf): + """Generate system identifier construction for specified node + """ + format_txt = self.sc_addrs[keynode_hypermedia_format_txt_str] + nrel_format = self.sc_addrs[keynode_hypermedia_nrel_format_str] - assert addr is not None - # generate identifier relation - generateSystemIdentifier(addr, system_idtf) - - #sc_helper_shutdown() - -# ------------------------------------------------ - -if __name__ == "__main__": - - print "Default encoding: %s" % sys.getdefaultencoding() - - if len(sys.argv) < 3: - print "Usage: python builder.py " - sys.exit(0) + idtf_data = str(idtf) + stream = sc_stream_memory_new(idtf_data, len(idtf_data), SC_STREAM_READ, False) - global input_path - global output_path - - input_path = sys.argv[1] - output_path = sys.argv[2] - - if os.path.exists(output_path): - shutil.rmtree(output_path) - - sc_memory_initialize(output_path, "") - - conv = converter.Converter() - conv.parse_directory(input_path) + assert stream is not None + + results_list = None + results_count = None + idtf_link = None + + idtf_link = sc_memory_link_new() + sc_memory_set_link_content(idtf_link, stream) + sc_stream_free(stream) + + # link nodes + arc_addr = sc_memory_arc_new(sc_type_arc_common | sc_type_const, el_addr, idtf_link) + sc_memory_arc_new(sc_type_arc_pos_const_perm, nrel_idtf_addr, arc_addr) + + # setup format + arc_addr = sc_memory_arc_new(sc_type_arc_common | sc_type_const, idtf_link, format_txt) + sc_memory_arc_new(sc_type_arc_pos_const_perm, nrel_format, arc_addr) + + self.created_links += 1 + self.created_arcs += 2 - # determine types of all objects - print "Determine list of all objects and their types..." - for triple in conv.triples: - for idx in xrange(3): - determineElementType(triple[idx], idx == 1) - - print "\tFound %d elements" % len(sc_types) + def generateIdentifiers(self): + + global nrel_idtf_addr + + # create 'nrel_system_identifier' keynode if it doesn't exist + nrel_idtf_str = u'hypermedia_nrel_system_identifier' + nrel_idtf_data = str(nrel_idtf_str) + + #sc_helper_init() + + try: + nrel_idtf_addr = self.sc_addrs[nrel_idtf_str] + except: + raise "You need to define system identifier keynode in scs" - # process triples - print "Resolve sc-addrs..." - for triple in conv.triples: - subject = triple[0] - object = triple[2] - predicate = triple[1] + assert nrel_idtf_addr is not None - # if first element in triple is an element type set, then change type of sc-element - if idtfToType.has_key(subject): + for idtf, addr in self.sc_addrs.items(): + + # extract object identifier + system_idtf = idtf + if self.checkIdtfWithPrefix(system_idtf): + system_idtf = self.splitIdtf(system_idtf)[1] + + if system_idtf.startswith('.') or (system_idtf.startswith('"') and system_idtf.endswith('"')) or system_idtf.startswith('_.'): + continue - idtf_type = idtfToType[subject] - new_type = sc_types[object] | idtf_type - # todo add types conflict checking - if (idtf_type & sc_type_constancy_mask != 0): - new_type = (idtf_type & sc_type_constancy_mask) | (new_type & ~sc_type_constancy_mask) - sc_types[object] = new_type + # temporary hack + if system_idtf == nrel_idtf_str: + continue + + #print "\tSetup for %s, %d: %d" % (idtf, addr.seg, addr.offset) + #print system_idtf + + assert addr is not None + # generate identifier relation + self.generateSystemIdentifier(addr, system_idtf) + + #sc_helper_shutdown() + + # ------------------------------------------------ + + def process(self, input_path, output_path): + + self.input_path = input_path + self.output_path = output_path + + if os.path.exists(self.output_path): + shutil.rmtree(self.output_path) + + sc_memory_initialize(self.output_path, "") + + self.conv = converter.Converter() + self.conv.parse_directory(input_path) + + # determine types of all objects + print "Determine list of all objects and their types..." + for triple in self.conv.triples: + subject = triple[0] + object = triple[2] + predicate = triple[1] - resolveScAddr(object) - continue - - resolveScAddr(subject) - resolveScAddr(object) - - # store arc - arc = ScArc() - arc.begin = subject - arc.end = object - arc.type = sc_types[predicate] - - sc_arcs[predicate] = arc - - # now create arcs, while there are any arcs not created, - # or any of them couldn't be created anymore - print "Create arcs..." - created = True - while len(sc_arcs) > 0 and created: - created = False - - created_list = [] - - # iterate all arcs that wasn't created and try to create them - for item in sc_arcs.iteritems(): - idtf, arc = item + self.determineElementType(subject, False) + self.determineElementType(predicate, True) + self.determineElementType(object, False) - # determine if begin and end arc elements created - begin_addr = None - end_addr = None + # if first element in triple is an element type set, then change type of sc-element + if idtfToType.has_key(subject): + + idtf_type = idtfToType[subject] + new_type = self.sc_types[object] | idtf_type + # todo add types conflict checking + if (idtf_type & sc_type_constancy_mask != 0): + new_type = (idtf_type & sc_type_constancy_mask) | (new_type & ~sc_type_constancy_mask) + + self.sc_types[object] = new_type + + print "\tFound %d elements" % len(self.sc_types) + + #print self.sc_types + + # process triples + print "Resolve sc-addrs..." + for triple in self.conv.triples: + subject = triple[0] + object = triple[2] + predicate = triple[1] - try: - begin_addr = sc_addrs[arc.begin] - end_addr = sc_addrs[arc.end] - except: + # if first element in triple is an element type set, then change type of sc-element + if idtfToType.has_key(subject): + self.resolveScAddr(object) continue - if (arc.type & sc_type_const and arc.type & sc_type_var): - print "Invalid type %s" % idtf - # create new arc - addr = sc_memory_arc_new(arc.type, begin_addr, end_addr) - sc_addrs[idtf] = addr - created_list.append(idtf) - - global created_arcs - created_arcs += 1 + self.resolveScAddr(subject) + self.resolveScAddr(object) - created = (len(created_list) > 0) - # remove created arcs from map - for arc in created_list: - sc_arcs.pop(arc) + # store arc + arc = ScArc() + arc.begin = subject + arc.end = object + arc.type = self.sc_types[predicate] + + self.sc_arcs[predicate] = arc + # now create arcs, while there are any arcs not created, + # or any of them couldn't be created anymore + print "Create arcs..." + created = True + while len(self.sc_arcs) > 0 and created: + created = False - # generate system identifiers - print "Setup system identifiers..." - generateIdentifiers() - - sc_memory_shutdown() - - # write list of arcs, that wasn't created - for idtf, arc in sc_arcs.items(): - print "Arc %s wasn't created" % idtf - try: - print "Begin: %s" % str(sc_addrs[arc.begin]) - print "End: %s" % str(sc_addrs[arc.end]) - except: - continue + created_list = [] - all_count = created_links + created_nodes + created_arcs - print "Statistics:" - print "\tCreated nodes: %d (%.03f%%)" % (created_nodes, float(created_nodes) / all_count * 100) - print "\tCreated links: %d (%.03f%%)" % (created_links, float(created_links) / all_count * 100) - print "\tCreated arcs: %d (%.03f%%)" % (created_arcs, float(created_arcs) / all_count * 100) - print "\tTotal: %d" % all_count + # iterate all arcs that wasn't created and try to create them + for item in self.sc_arcs.iteritems(): + idtf, arc = item + + # determine if begin and end arc elements created + begin_addr = None + end_addr = None + + try: + begin_addr = self.sc_addrs[arc.begin] + end_addr = self.sc_addrs[arc.end] + except: + continue + + if (arc.type & sc_type_const and arc.type & sc_type_var): + print "Invalid type %s" % idtf + # create new arc + addr = sc_memory_arc_new(arc.type, begin_addr, end_addr) + self.sc_addrs[idtf] = addr + created_list.append(idtf) + + self.created_arcs += 1 + + created = (len(created_list) > 0) + # remove created arcs from map + for arc in created_list: + self.sc_arcs.pop(arc) + + + # generate system identifiers + print "Setup system identifiers..." + self.generateIdentifiers() + + sc_memory_shutdown() + + # write list of arcs, that wasn't created + for idtf, arc in self.sc_arcs.items(): + print "Arc %s wasn't created" % idtf + try: + print "Begin: %s" % str(self.sc_addrs[arc.begin]) + print "End: %s" % str(self.sc_addrs[arc.end]) + except: + continue + + all_count = self.created_links + self.created_nodes + self.created_arcs + print "Statistics:" + print "\tCreated nodes: %d (%.03f%%)" % (self.created_nodes, float(self.created_nodes) / all_count * 100) + print "\tCreated links: %d (%.03f%%)" % (self.created_links, float(self.created_links) / all_count * 100) + print "\tCreated arcs: %d (%.03f%%)" % (self.created_arcs, float(self.created_arcs) / all_count * 100) + print "\tTotal: %d" % all_count + + +if __name__ == "__main__": - print "" + print "Default encoding: %s" % sys.getdefaultencoding() + + if len(sys.argv) < 3: + print "Usage: python builder.py " + sys.exit(0) + + builder = ScBuilder() + builder.process(sys.argv[1], sys.argv[2]) diff --git a/tools/scs/converter.py b/tools/scs/converter.py index ec48a945..f8ddbf38 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -30,7 +30,7 @@ import scs_parser from sre_parse import parse_template -ParserElement.enablePackrat() +#ParserElement.enablePackrat() encoding = "utf-8" reload(sys) @@ -400,6 +400,7 @@ def processContentGroup(self, group): objects.append(triple[idx]) contour = self.generate_contour_idtf() + self.append_sentence(u'sc_node_struct', self.generate_arc_idtf('->', False), contour, False) for obj in objects: self.append_sentence(contour, self.generate_arc_idtf('->', False), obj, False) From 896b2305fd36fb97285cb4e0ac04a44c0820def4 Mon Sep 17 00:00:00 2001 From: deniskoronchik Date: Mon, 29 Apr 2013 14:10:56 +0300 Subject: [PATCH 29/84] add support of triples --- tools/scs/converter.py | 14 +++++++++++++- tools/scs/tests/incl.scsi | 2 +- tools/scs/tests/test.scs | 2 ++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/tools/scs/converter.py b/tools/scs/converter.py index f8ddbf38..0455269e 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -221,6 +221,8 @@ def resolve_identifier(self, group): alias = self.generate_oset_idtf() elif isinstance(group, scs_parser.SetGroup): alias = self.generate_set_idtf() + elif isinstance(group, scs_parser.TripleGroup): + alias = self.generate_arc_idtf(group.predicate, True) self.aliases[key] = alias @@ -333,7 +335,17 @@ def processInternalListGroup(self, group): return group def processTripleGroup(self, group): - return group + + self.parse_tree(group.subject) + self.parse_tree(group.object) + + pred = self.resolve_identifier(group) + subj = self.resolve_identifier(group.subject) + obj = self.resolve_identifier(group.object) + + self.append_sentence(subj, pred, obj, self.check_predicate_mirror(group.predicate)) + + return pred def processAliasGroup(self, group): """Just resolve identifier for alias diff --git a/tools/scs/tests/incl.scsi b/tools/scs/tests/incl.scsi index daf028ef..487567b6 100644 --- a/tools/scs/tests/incl.scsi +++ b/tools/scs/tests/incl.scsi @@ -1,2 +1,2 @@ incl1 -> incl2;; -[* debug *] <- debug_contour;; +[* debug -> a;; *] <- debug_contour;; diff --git a/tools/scs/tests/test.scs b/tools/scs/tests/test.scs index 932af9c5..773ee752 100644 --- a/tools/scs/tests/test.scs +++ b/tools/scs/tests/test.scs @@ -44,3 +44,5 @@ Partition_SCs_code <= nrel_decomposition: y -> [*^"file://incl.scsi"*];; + +const_arc -> (v -> (g <= n)) (* <- tested;; *);; \ No newline at end of file From e0b2fe0da4a738c6717cbfb9405fd360a9772924 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Mon, 1 Jul 2013 01:28:52 +0300 Subject: [PATCH 30/84] fixed bug, when metainformation about files included into contours, when translating from scs --- tools/scs/builder.py | 10 ++++++++-- tools/scs/converter.py | 24 +++++++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/tools/scs/builder.py b/tools/scs/builder.py index 40f7c3d4..eadd767d 100644 --- a/tools/scs/builder.py +++ b/tools/scs/builder.py @@ -350,7 +350,12 @@ def process(self, input_path, output_path): # determine types of all objects print "Determine list of all objects and their types..." - for triple in self.conv.triples: + + triples = [] + triples.extend(self.conv.triples) + triples.extend(self.conv.metaTriples) + + for triple in triples: subject = triple[0] object = triple[2] predicate = triple[1] @@ -376,7 +381,8 @@ def process(self, input_path, output_path): # process triples print "Resolve sc-addrs..." - for triple in self.conv.triples: + + for triple in triples: subject = triple[0] object = triple[2] predicate = triple[1] diff --git a/tools/scs/converter.py b/tools/scs/converter.py index 0455269e..3b4eb956 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -154,7 +154,8 @@ def __init__(self): } self.comments = {} - self.triples = [] + self.triples = [] # general triples + self.metaTriples = [] # meta triples, used to store meta information. They not append into contours, because they builded by converter # map of contents, that need to be written self.link_contents = {} @@ -243,8 +244,8 @@ def buildFormatRelation(self, linkIdtf, ext): """ fmt_idtf = u'hypermedia_format_' + ext arc_idtf = self.generate_arc_idtf(u'=>', True) - self.append_sentence(linkIdtf, arc_idtf, fmt_idtf, False) - self.append_sentence(u'hypermedia_nrel_format', self.generate_arc_idtf(u'->', True), arc_idtf, False) + self.append_sentence(linkIdtf, arc_idtf, fmt_idtf, False, True) + self.append_sentence(u'hypermedia_nrel_format', self.generate_arc_idtf(u'->', True), arc_idtf, False, True) def resolve_synonym(self, idtf): if self.synonyms.has_key(idtf): @@ -256,17 +257,22 @@ def check_predicate_mirror(self, predicate): return (predicate in mirror_connectors) - def append_sentence(self, subject, predicate, object, isMirrored): + def append_sentence(self, subject, predicate, object, isMirrored, isMeta = False): """Appends new scs-level 1 sentence into list """ assert isinstance(subject, str) or isinstance(subject, unicode) assert isinstance(object, str) or isinstance(object, unicode) assert isinstance(predicate, str) or isinstance(predicate, unicode) + triples = self.triples + + if isMeta: + triples = self.metaTriples + if not isMirrored: - self.triples.append((subject, predicate, object)) + triples.append((subject, predicate, object)) else: - self.triples.append((object, predicate, subject)) + triples.append((object, predicate, subject)) # --------------------------------------- def processSimpleIdentifierGroup(self, group): @@ -396,6 +402,7 @@ def processContentGroup(self, group): self.link_contents = converter.link_contents self.synonyms = converter.synonyms self.triples.extend(converter.triples) + self.metaTriples.extend(converter.metaTriples) self.aliases = converter.aliases self.set_count = converter.set_count self.oset_count = converter.oset_count @@ -577,8 +584,11 @@ def write_to_fs(self, path): os.makedirs(path) count = 0 + triples = [] + triples.extend(self.triples) + triples.extend(self.metaTriples) with codecs.open(os.path.join(path, "data.scs"), "w", "utf-8") as output: - for t in self.triples: + for t in triples: if self.comments.has_key(count): output.write('\n/* --- %s --- */\n' % self.comments[count]) From e49b149fb8e2c4a5d721b0a1bb77d7bf98868316 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Tue, 29 Oct 2013 22:52:31 +0300 Subject: [PATCH 31/84] fixed bug with file links processing in simple triples --- tools/scs/converter.py | 3 +++ tools/scs/scs_parser.py | 3 ++- tools/scs/tests/test.scs | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/tools/scs/converter.py b/tools/scs/converter.py index 3b4eb956..5d5f732d 100644 --- a/tools/scs/converter.py +++ b/tools/scs/converter.py @@ -304,6 +304,9 @@ def processKeywordGroup(self, group): def processSimpleSentenceGroup(self, group): """Process scs-level 1 sentences """ + self.parse_tree(group.subject) + self.parse_tree(group.object) + subject_idtf = self.resolve_identifier(group.subject) object_idtf = self.resolve_identifier(group.object) arc_idtf = group.predicate.value diff --git a/tools/scs/scs_parser.py b/tools/scs/scs_parser.py index cf328183..d0a1a1e9 100644 --- a/tools/scs/scs_parser.py +++ b/tools/scs/scs_parser.py @@ -26,7 +26,7 @@ from pyparsing import Word, Literal, Forward, Regex, Group, ZeroOrMore, SkipTo, ParserElement from pyparsing import OneOrMore, srange, Keyword, QuotedString, ParseResults, Optional, cStyleComment -ParserElement.enablePackrat() +#ParserElement.enablePackrat() reKeyword = r'/!\*\s*keyword:\s*([a-zA-Z0-9_]+)\s*\*/' @@ -385,6 +385,7 @@ def syntax(): rpar_trf = Literal(u']').suppress() lpar_int = Literal(u'(*').suppress() rpar_int = Literal(u'*)').suppress() + plus = Literal(u'+').suppress() # comments comment_keyword = Regex(reKeyword).setParseAction(KeywordGroup) diff --git a/tools/scs/tests/test.scs b/tools/scs/tests/test.scs index 773ee752..e9cb2fd2 100644 --- a/tools/scs/tests/test.scs +++ b/tools/scs/tests/test.scs @@ -2,7 +2,7 @@ 5 -> 6: 7: tr; ty;; 6 <- 8: re; gg; hj;; -file -> "file://image.png";; +file | rac | "file://image.png";; set -> attr_set: { sum: 1; result: 2; 3};; set ~> < attr1: 1; attr2: 2; attr3: 3>;; @@ -45,4 +45,4 @@ Partition_SCs_code <= nrel_decomposition: y -> [*^"file://incl.scsi"*];; -const_arc -> (v -> (g <= n)) (* <- tested;; *);; \ No newline at end of file +const_arc -> (v -> (g <= n)) (* <- tested;; *);; From 3e55f8cc5b1b78e3a79828dfd8d929a144f3ad09 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Sun, 30 Mar 2014 00:19:49 +0300 Subject: [PATCH 32/84] work on issue #1 --- .gitmodules | 9 - sc-machine | 1 - sc-web | 1 - tools/kbe | 1 - tools/scs/builder.py | 476 ----------------------------- tools/scs/convert.bat | 1 - tools/scs/converter.py | 625 -------------------------------------- tools/scs/scs_parser.py | 462 ---------------------------- tools/scs/tests/image.png | Bin 24361 -> 0 bytes tools/scs/tests/incl.scsi | 2 - tools/scs/tests/test.scs | 48 --- 11 files changed, 1626 deletions(-) delete mode 100644 .gitmodules delete mode 160000 sc-machine delete mode 160000 sc-web delete mode 160000 tools/kbe delete mode 100644 tools/scs/builder.py delete mode 100644 tools/scs/convert.bat delete mode 100644 tools/scs/converter.py delete mode 100644 tools/scs/scs_parser.py delete mode 100644 tools/scs/tests/image.png delete mode 100644 tools/scs/tests/incl.scsi delete mode 100644 tools/scs/tests/test.scs diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 0e5796b5..00000000 --- a/.gitmodules +++ /dev/null @@ -1,9 +0,0 @@ -[submodule "tools/kbe"] - path = tools/kbe - url = git@github.com:deniskoronchik/kbe.git -[submodule "sc-machine"] - path = sc-machine - url = git@github.com:deniskoronchik/sc-machine.git -[submodule "sc-web"] - path = sc-web - url = git@github.com:deniskoronchik/sc-web.git diff --git a/sc-machine b/sc-machine deleted file mode 160000 index eae1520f..00000000 --- a/sc-machine +++ /dev/null @@ -1 +0,0 @@ -Subproject commit eae1520f1905ca5c2350b1a687cd433f1752f84d diff --git a/sc-web b/sc-web deleted file mode 160000 index cbbf5ed4..00000000 --- a/sc-web +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cbbf5ed4030205cb19c0de7c1268fb21a26dfddf diff --git a/tools/kbe b/tools/kbe deleted file mode 160000 index e9200408..00000000 --- a/tools/kbe +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e9200408ac0966850b84002309e78e344479717d diff --git a/tools/scs/builder.py b/tools/scs/builder.py deleted file mode 100644 index eadd767d..00000000 --- a/tools/scs/builder.py +++ /dev/null @@ -1,476 +0,0 @@ -# -*- coding: utf-8 -*- -""" ------------------------------------------------------------------------------ -This source file is part of OSTIS (Open Semantic Technology for Intelligent Systems) -For the latest info, see http://www.ostis.net - -Copyright (c) 2012 OSTIS - -OSTIS is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -OSTIS 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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with OSTIS. If not, see . ------------------------------------------------------------------------------ -""" - -import os, sys, shutil -import codecs -import converter -import ctypes -from pysc import * -from pysc import _sc_addr - -# keynode identifires -keynode_hypermedia_format_txt_str = u"hypermedia_format_txt" -keynode_hypermedia_nrel_format_str = u"hypermedia_nrel_format" - - -encoding = "utf-8" -reload(sys) -sys.setdefaultencoding(encoding) -sys.stdout = codecs.getwriter(encoding)(sys.stdout, errors = "replace") -sys.stderr = codecs.getwriter(encoding)(sys.stderr, errors = "replace") - -# mapping of identifier prefix into sc-type -arcPrefixToType = { - u"sc_arc_common" : sc_type_arc_common, - u"sc_arc_main": sc_type_arc_pos_const_perm, - u"sc_edge" : sc_type_edge_common, - u"sc_arc_access": sc_type_arc_access, - } - -# mapping of set identifiers into sc-types -idtfToType = { - u"sc_edge_const": sc_type_edge_common | sc_type_const, - u"sc_edge_var": sc_type_edge_common | sc_type_var, - u"sc_arc_common_const": sc_type_arc_common | sc_type_const, - u"sc_arc_common_var": sc_type_arc_common | sc_type_var, - - u"sc_arc_access_var_pos_perm": sc_type_arc_access | sc_type_var | sc_type_arc_pos | sc_type_arc_perm, - u"sc_arc_access_const_neg_perm": sc_type_arc_access | sc_type_const | sc_type_arc_neg | sc_type_arc_perm, - u"sc_arc_access_var_neg_perm": sc_type_arc_access | sc_type_var | sc_type_arc_neg | sc_type_arc_perm, - u"sc_arc_access_const_fuz_perm": sc_type_arc_access | sc_type_const | sc_type_arc_fuz | sc_type_arc_perm, - u"sc_arc_access_var_fuz_perm": sc_type_arc_access | sc_type_var | sc_type_arc_fuz | sc_type_arc_perm, - - u"sc_arc_access_const_pos_temp": sc_type_arc_access | sc_type_const | sc_type_arc_pos | sc_type_arc_temp, - u"sc_arc_access_var_pos_temp": sc_type_arc_access | sc_type_var | sc_type_arc_pos | sc_type_arc_temp, - u"sc_arc_access_const_neg_temp": sc_type_arc_access | sc_type_const | sc_type_arc_neg | sc_type_arc_temp, - u"sc_arc_access_var_neg_temp": sc_type_arc_access | sc_type_var | sc_type_arc_neg | sc_type_arc_temp, - u"sc_arc_access_const_fuz_temp": sc_type_arc_access | sc_type_const | sc_type_arc_fuz | sc_type_arc_temp, - u"sc_arc_access_var_fuz_temp": sc_type_arc_access | sc_type_var | sc_type_arc_fuz | sc_type_arc_temp, - - # nodes - u"sc_const": sc_type_const, - u"sc_var": sc_type_var, - u"sc_node_not_binary_tuple": sc_type_node_tuple, - u"sc_node_struct": sc_type_node_struct, - u"sc_node_role_relation": sc_type_node_role, - u"sc_node_norole_relation": sc_type_node_norole, - u"sc_node_not_relation": sc_type_node_class, - u"sc_node_abstract": sc_type_node_abstract, - u"sc_node_material": sc_type_node_material, - } - - -# -------------------------------------------------- - -class ScElement: - def __init__(self): - self.type = sc_type_node - self.idtf = 0 - -class ScNode(ScElement): - def __init__(self): - ScElement.__init__(self) - -class ScArc(ScElement): - def __init__(self): - ScElement.__init__(self) - self.sc_addr = None - self.begin = None - self.end = None - -class ScLink(ScElement): - def __init__(self): - ScElement.__init__(self) - -# -------------------------------------------------- - -class ScBuilder: - - def __init__(self): - # mapping identifiers into sc-addrs - self.sc_addrs = {} - # map of sc-element types - self.sc_types = {} - # map of sc-arc, that need to be created - self.sc_arcs = {} - self.conv = None - - # statistics - self.created_nodes = 0 - self.created_links = 0 - self.created_arcs = 0 - - self.nrel_idtf_addr = None - - self.input_path = None - self.output_path = None - - def checkIdtfWithPrefix(self, idtf): - """Check if specified identifier contains type - """ - return idtf.count(u'#') > 0 - - def checkIdtfIsLink(self, idtf): - """Checks if specified idtf is and sc-link - """ - return len(idtf) > 1 and idtf[0] == u'"' and idtf[-1] == u'"' - - def checkIdtfIsArc(self, idtf): - """Check if specified identifier is an arc identifier. - This function analyze just identifier - """ - if self.checkIdtfWithPrefix(idtf): - preffix, el_idtf = self.splitIdtf(idtf) - if arcPrefixToType.has_key(preffix): - return True - - return False - - def splitIdtf(self, idtf): - """Split identifier with prefix to type and identifier - """ - assert self.checkIdtfWithPrefix(idtf) - - res = idtf.split('#') - return (res[0], res[1]) - - - def determineArcType(self, arc_idtf): - """Determine type of predicate - """ - if self.checkIdtfWithPrefix(arc_idtf): - prefix, idtf = self.splitIdtf(arc_idtf) - return arcPrefixToType[prefix] - - return sc_type_arc_common - - def determineElementType(self, idtf, isPredicate): - """Determines element type and store it in types map - """ - oldType = None - try: - oldType = self.sc_types[idtf] - except: - pass - - isVariable = idtf.startswith(u"_") - - newType = None - if self.checkIdtfIsLink(idtf): - newType = sc_type_link - else: - if self.checkIdtfIsArc(idtf): - newType = self.determineArcType(idtf) - - if newType is None: - if isPredicate: - newType = sc_type_arc_common - else: - newType = sc_type_node - - if isVariable: - newType = newType | sc_type_var - else: - newType = newType | sc_type_const - - if oldType is not None: - # determine if new element type more common then old type - if oldType == (newType & oldType): - self.sc_types[idtf] = newType - else: - self.sc_types[idtf] = newType - - def resolveLinkPath(self, idtf): - """Resolve sc-link file path - """ - path = idtf[1:-1] - path = path.replace("file://", "") - - return os.path.join(input_path, path) - - - def createNodeOrLink(self, elIdtf, elType): - """Create sc-node or sc-link in memory - """ - if elType & sc_type_link: - addr = sc_memory_link_new() - self.created_links += 1 - - - # setup link data - path = elIdtf[1:-1] - path = unicode(path.replace("file://", "")) - - #print path, addr.seg, addr.offset - stream = None - if self.conv.link_copy_contents.has_key(path): - cont_path = str(self.conv.link_copy_contents[path]) - if os.path.exists(cont_path): - stream = sc_stream_file_new(cont_path, SC_STREAM_READ) - else: - print "File doesn't exist '%s'" % cont_path - elif self.conv.link_contents.has_key(path): - data = str(self.conv.link_contents[path]) - stream = sc_stream_memory_new(data, len(data), SC_STREAM_READ, False) - - if stream is None: - print "Can't setup content from path %s" % path - else: - res = sc_memory_set_link_content(addr, stream) - if res != SC_RESULT_OK: - print "Can't setup link data for %s" % (str(elIdtf)) - sc_stream_free(stream) - - - elif elType & sc_type_node: - - addr = sc_memory_node_new(elType) - self.created_nodes += 1 - - else: - raise "Unknown type" - - self.sc_addrs[elIdtf] = addr - - def resolveScAddr(self, idtf): - """Resolve sc-addr of element if it possible - """ - if self.sc_addrs.has_key(idtf): - return - - obj_type = self.sc_types[idtf] - # create subject and object if them aren't an arcs - if not (obj_type & sc_type_arc_mask): - self.createNodeOrLink(idtf, obj_type) - - def generateSystemIdentifier(self, el_addr, idtf): - """Generate system identifier construction for specified node - """ - format_txt = self.sc_addrs[keynode_hypermedia_format_txt_str] - nrel_format = self.sc_addrs[keynode_hypermedia_nrel_format_str] - - idtf_data = str(idtf) - stream = sc_stream_memory_new(idtf_data, len(idtf_data), SC_STREAM_READ, False) - - assert stream is not None - - results_list = None - results_count = None - idtf_link = None - - idtf_link = sc_memory_link_new() - sc_memory_set_link_content(idtf_link, stream) - sc_stream_free(stream) - - # link nodes - arc_addr = sc_memory_arc_new(sc_type_arc_common | sc_type_const, el_addr, idtf_link) - sc_memory_arc_new(sc_type_arc_pos_const_perm, nrel_idtf_addr, arc_addr) - - # setup format - arc_addr = sc_memory_arc_new(sc_type_arc_common | sc_type_const, idtf_link, format_txt) - sc_memory_arc_new(sc_type_arc_pos_const_perm, nrel_format, arc_addr) - - self.created_links += 1 - self.created_arcs += 2 - - def generateIdentifiers(self): - - global nrel_idtf_addr - - # create 'nrel_system_identifier' keynode if it doesn't exist - nrel_idtf_str = u'hypermedia_nrel_system_identifier' - nrel_idtf_data = str(nrel_idtf_str) - - #sc_helper_init() - - try: - nrel_idtf_addr = self.sc_addrs[nrel_idtf_str] - except: - raise "You need to define system identifier keynode in scs" - - assert nrel_idtf_addr is not None - - for idtf, addr in self.sc_addrs.items(): - - # extract object identifier - system_idtf = idtf - if self.checkIdtfWithPrefix(system_idtf): - system_idtf = self.splitIdtf(system_idtf)[1] - - if system_idtf.startswith('.') or (system_idtf.startswith('"') and system_idtf.endswith('"')) or system_idtf.startswith('_.'): - continue - - # temporary hack - if system_idtf == nrel_idtf_str: - continue - - #print "\tSetup for %s, %d: %d" % (idtf, addr.seg, addr.offset) - #print system_idtf - - assert addr is not None - # generate identifier relation - self.generateSystemIdentifier(addr, system_idtf) - - #sc_helper_shutdown() - - # ------------------------------------------------ - - def process(self, input_path, output_path): - - self.input_path = input_path - self.output_path = output_path - - if os.path.exists(self.output_path): - shutil.rmtree(self.output_path) - - sc_memory_initialize(self.output_path, "") - - self.conv = converter.Converter() - self.conv.parse_directory(input_path) - - # determine types of all objects - print "Determine list of all objects and their types..." - - triples = [] - triples.extend(self.conv.triples) - triples.extend(self.conv.metaTriples) - - for triple in triples: - subject = triple[0] - object = triple[2] - predicate = triple[1] - - self.determineElementType(subject, False) - self.determineElementType(predicate, True) - self.determineElementType(object, False) - - # if first element in triple is an element type set, then change type of sc-element - if idtfToType.has_key(subject): - - idtf_type = idtfToType[subject] - new_type = self.sc_types[object] | idtf_type - # todo add types conflict checking - if (idtf_type & sc_type_constancy_mask != 0): - new_type = (idtf_type & sc_type_constancy_mask) | (new_type & ~sc_type_constancy_mask) - - self.sc_types[object] = new_type - - print "\tFound %d elements" % len(self.sc_types) - - #print self.sc_types - - # process triples - print "Resolve sc-addrs..." - - for triple in triples: - subject = triple[0] - object = triple[2] - predicate = triple[1] - - # if first element in triple is an element type set, then change type of sc-element - if idtfToType.has_key(subject): - self.resolveScAddr(object) - continue - - self.resolveScAddr(subject) - self.resolveScAddr(object) - - # store arc - arc = ScArc() - arc.begin = subject - arc.end = object - arc.type = self.sc_types[predicate] - - self.sc_arcs[predicate] = arc - - # now create arcs, while there are any arcs not created, - # or any of them couldn't be created anymore - print "Create arcs..." - created = True - while len(self.sc_arcs) > 0 and created: - created = False - - created_list = [] - - # iterate all arcs that wasn't created and try to create them - for item in self.sc_arcs.iteritems(): - idtf, arc = item - - # determine if begin and end arc elements created - begin_addr = None - end_addr = None - - try: - begin_addr = self.sc_addrs[arc.begin] - end_addr = self.sc_addrs[arc.end] - except: - continue - - if (arc.type & sc_type_const and arc.type & sc_type_var): - print "Invalid type %s" % idtf - # create new arc - addr = sc_memory_arc_new(arc.type, begin_addr, end_addr) - self.sc_addrs[idtf] = addr - created_list.append(idtf) - - self.created_arcs += 1 - - created = (len(created_list) > 0) - # remove created arcs from map - for arc in created_list: - self.sc_arcs.pop(arc) - - - # generate system identifiers - print "Setup system identifiers..." - self.generateIdentifiers() - - sc_memory_shutdown() - - # write list of arcs, that wasn't created - for idtf, arc in self.sc_arcs.items(): - print "Arc %s wasn't created" % idtf - try: - print "Begin: %s" % str(self.sc_addrs[arc.begin]) - print "End: %s" % str(self.sc_addrs[arc.end]) - except: - continue - - all_count = self.created_links + self.created_nodes + self.created_arcs - print "Statistics:" - print "\tCreated nodes: %d (%.03f%%)" % (self.created_nodes, float(self.created_nodes) / all_count * 100) - print "\tCreated links: %d (%.03f%%)" % (self.created_links, float(self.created_links) / all_count * 100) - print "\tCreated arcs: %d (%.03f%%)" % (self.created_arcs, float(self.created_arcs) / all_count * 100) - print "\tTotal: %d" % all_count - - -if __name__ == "__main__": - - print "Default encoding: %s" % sys.getdefaultencoding() - - if len(sys.argv) < 3: - print "Usage: python builder.py " - sys.exit(0) - - builder = ScBuilder() - builder.process(sys.argv[1], sys.argv[2]) diff --git a/tools/scs/convert.bat b/tools/scs/convert.bat deleted file mode 100644 index 0ea96118..00000000 --- a/tools/scs/convert.bat +++ /dev/null @@ -1 +0,0 @@ -converter.py tests output \ No newline at end of file diff --git a/tools/scs/converter.py b/tools/scs/converter.py deleted file mode 100644 index 5d5f732d..00000000 --- a/tools/scs/converter.py +++ /dev/null @@ -1,625 +0,0 @@ -# -*- coding: utf-8 -*- - -""" ------------------------------------------------------------------------------ -This source file is part of OSTIS (Open Semantic Technology for Intelligent Systems) -For the latest info, see http://www.ostis.net - -Copyright (c) 2012 OSTIS - -OSTIS is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -OSTIS 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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with OSTIS. If not, see . ------------------------------------------------------------------------------ -""" - -import os, sys, shutil -import codecs -import re, sys, traceback -from pyparsing import Word, Literal, Forward, Regex, Group, ZeroOrMore, SkipTo, ParserElement -from pyparsing import OneOrMore, srange, Keyword, QuotedString, ParseResults, Optional, cStyleComment, ParseException -import scs_parser -from sre_parse import parse_template - -#ParserElement.enablePackrat() - -encoding = "utf-8" -reload(sys) -sys.setdefaultencoding(encoding) -sys.stdout = codecs.getwriter(encoding)(sys.stdout, errors = "replace") -sys.stderr = codecs.getwriter(encoding)(sys.stderr, errors = "replace") - -reKeyword = r'/!\*\s*keyword:\s*([a-zA-Z0-9_]+)\s*\*/' - -arc_id = 0 - -mirror_connectors = [ - u'<', - u'<..', - u'<-', - u'<=', - u'<|-', - u'" : u"sc_arc_common", - u"<" : u"sc_arc_common", - u"->": u"sc_arc_main", - u"<-": u"sc_arc_main", - u"<>": u"sc_edge", - u"..>": u"sc_arc_access", - u"<..": u"sc_arc_access", - u'<=>': u"sc_edge", - u'_<=>': u"sc_edge", - u'=>': u"sc_arc_common", - u'<=': u"sc_arc_common", - u'=>': u"sc_arc_common", - u'<=': u"sc_arc_common", - u'_->': u"sc_arc_access", - u'_<-': u"sc_arc_access", - u'-|>': u"sc_arc_access", - u'_-|>': u"sc_arc_access", - u'<|-': u"sc_arc_access", - u'_<|-': u"sc_arc_access", - u'-/>': u"sc_arc_access", - u'_-/>': u"sc_arc_access", - u'': u"sc_arc_access", - u'_~>': u"sc_arc_access", - u'<~': u"sc_arc_access", - u'_<~': u"sc_arc_access", - u'~|>': u"sc_arc_access", - u'_~|>': u"sc_arc_access", - u'<|~': u"sc_arc_access", - u'_<|~': u"sc_arc_access", - u'~/>': u"sc_arc_access", - u'_~/>': u"sc_arc_access", - u'': u"sc_edge_const", - u'_<=>': u"sc_edge_var", - u'=>': u"sc_arc_common_const", - u'<=': u"sc_arc_common_const", - u'_=>': u"sc_arc_common_var", - u'_<=': u"sc_arc_common_var", - u'_->': u"sc_arc_access_var_pos_perm", - u'_<-': u"sc_arc_access_var_pos_perm", - u'-|>': u"sc_arc_access_const_neg_perm", - u'_-|>': u"sc_arc_access_var_neg_perm", - u'<|-': u"sc_arc_access_const_neg_perm", - u'_<|-': u"sc_arc_access_var_neg_perm", - u'-/>': u"sc_arc_access_const_fuz_perm", - u'_-/>': u"sc_arc_access_var_fuz_perm", - u'': u"sc_arc_access_const_pos_temp", - u'_~>': u"sc_arc_access_var_pos_temp", - u'<~': u"sc_arc_access_const_pos_temp", - u'_<~': u"sc_arc_access_var_pos_temp", - u'~|>': u"sc_arc_access_const_neg_temp", - u'_~|>': u"sc_arc_access_var_neg_temp", - u'<|~': u"sc_arc_access_const_neg_temp", - u'_<|~': u"sc_arc_access_var_neg_temp", - u'~/>': u"sc_arc_access_const_fuz_temp", - u'_~/>': u"sc_arc_access_var_fuz_temp", - u'', False), res, False) - - return res - - def resolve_identifier(self, group): - """Resolves identifiers for different groups - """ - - key = str(group) - if self.aliases.has_key(key): - return self.aliases[key] - - alias = str(group) - if isinstance(group, scs_parser.OSetGroup): - alias = self.generate_oset_idtf() - elif isinstance(group, scs_parser.SetGroup): - alias = self.generate_set_idtf() - elif isinstance(group, scs_parser.TripleGroup): - alias = self.generate_arc_idtf(group.predicate, True) - - self.aliases[key] = alias - - return alias - - def append_synonyms(self, idtf1, idtf2): - """Appends two identifiers as synonyms into map - """ - if self.synonyms.has_key(idtf1): - return - - self.synonyms[idtf1] = idtf2 - - def buildFormatRelation(self, linkIdtf, ext): - """Build relation between sc-link and it's format - @param linkIdtf: Identifier of sc-link - @param ext: File extension - """ - fmt_idtf = u'hypermedia_format_' + ext - arc_idtf = self.generate_arc_idtf(u'=>', True) - self.append_sentence(linkIdtf, arc_idtf, fmt_idtf, False, True) - self.append_sentence(u'hypermedia_nrel_format', self.generate_arc_idtf(u'->', True), arc_idtf, False, True) - - def resolve_synonym(self, idtf): - if self.synonyms.has_key(idtf): - return self.resolve_synonym(self.synonyms[idtf]) - - return idtf - - def check_predicate_mirror(self, predicate): - - return (predicate in mirror_connectors) - - def append_sentence(self, subject, predicate, object, isMirrored, isMeta = False): - """Appends new scs-level 1 sentence into list - """ - assert isinstance(subject, str) or isinstance(subject, unicode) - assert isinstance(object, str) or isinstance(object, unicode) - assert isinstance(predicate, str) or isinstance(predicate, unicode) - - triples = self.triples - - if isMeta: - triples = self.metaTriples - - if not isMirrored: - triples.append((subject, predicate, object)) - else: - triples.append((object, predicate, subject)) - - # --------------------------------------- - def processSimpleIdentifierGroup(self, group): - self.resolve_identifier(group) - - def processUrlGroup(self, group): - """Process url to link content data - """ - data_path = group.value[1:-1] - if data_path.startswith(u"file://"): - data_path = data_path.replace(u"file://", u"") - - path, tail = os.path.split(self.process_file) - abs_path = os.path.abspath(os.path.join(path, data_path)) - if self.contents_copy_link.has_key(abs_path): - group.value = '"file://%s"' % self.contents_copy_link[abs_path] - else: - link_idtf = self.generate_link_idtf() - self.link_copy_contents[link_idtf] = abs_path - self.contents_copy_link[abs_path] = link_idtf - group.value = '"file://%s"' % link_idtf - - path, ext = os.path.splitext(abs_path) - self.buildFormatRelation(group.value, ext[1:]) - - def processKeywordGroup(self, group): - return group - - def processSimpleSentenceGroup(self, group): - """Process scs-level 1 sentences - """ - self.parse_tree(group.subject) - self.parse_tree(group.object) - - subject_idtf = self.resolve_identifier(group.subject) - object_idtf = self.resolve_identifier(group.object) - arc_idtf = group.predicate.value - - self.append_sentence(subject_idtf, arc_idtf, object_idtf, False); - - def processIdtfWithIntGroup(self, group): - """Process identifier with internal sentence group - """ - self.parse_tree(group.idtf) - subject_idtf = self.resolve_identifier(group.idtf) - - internal_list = group.internal - if internal_list is not None: - for sentence in internal_list.sentences: - - for obj in sentence.object: - self.parse_tree(obj) - object_idtf = self.resolve_identifier(obj) - attributes = sentence.attrs - arc_idtf = self.generate_arc_idtf(sentence.predicate) - - self.append_sentence(subject_idtf, arc_idtf, object_idtf, self.check_predicate_mirror(sentence.predicate)) - - # write attributes - for attr in attributes: - attr_idtf = self.resolve_identifier(attr) - self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) - - - def processInternalGroup(self, group): - return group - - def processInternalListGroup(self, group): - return group - - def processTripleGroup(self, group): - - self.parse_tree(group.subject) - self.parse_tree(group.object) - - pred = self.resolve_identifier(group) - subj = self.resolve_identifier(group.subject) - obj = self.resolve_identifier(group.object) - - self.append_sentence(subj, pred, obj, self.check_predicate_mirror(group.predicate)) - - return pred - - def processAliasGroup(self, group): - """Just resolve identifier for alias - """ - return self.resolve_identifier(group) - - def processContentGroup(self, group): - """Store link content for saving - """ - result = None - if len(group.value) > 1 and group.value[0] == u'*' and group.value[-1] == u'*': - data = group.value[1:-1] - data_str = data - process_file = self.process_file - process_dir = self.process_dir - - if data.startswith(u'^'): - data = data[2:-1] - if data.startswith(u"file://"): - data = data.replace(u"file://", u"") - - - process_dir, tail = os.path.split(self.process_file) - process_file = os.path.join(process_dir, data) - f = open(process_file, "r") - data_str = f.read() - f.close() - - - - # create new converter and build data - converter = Converter() - converter.link_copy_contents = self.link_copy_contents - converter.link_contents = self.link_contents - converter.synonyms = self.synonyms - #converter.triples = self.triples - converter.aliases = self.aliases - converter.set_count = self.set_count - converter.oset_count = self.oset_count - converter.arc_count = self.arc_count - converter.link_count = self.link_count - converter.process_file = process_file - converter.process_dir = process_dir - - converter.parse_string(data_str) - - self.link_copy_contents = converter.link_copy_contents - self.link_contents = converter.link_contents - self.synonyms = converter.synonyms - self.triples.extend(converter.triples) - self.metaTriples.extend(converter.metaTriples) - self.aliases = converter.aliases - self.set_count = converter.set_count - self.oset_count = converter.oset_count - self.arc_count = converter.arc_count - self.link_count = converter.link_count - - # collect all created sc-elements to append them into contour - objects = [] - for triple in converter.triples: - if triple[0] in arc_types.values() or triple[0] in arc_keynodes.values(): - continue - for idx in xrange(len(triple)): - if not triple[idx] in objects: - objects.append(triple[idx]) - - contour = self.generate_contour_idtf() - self.append_sentence(u'sc_node_struct', self.generate_arc_idtf('->', False), contour, False) - for obj in objects: - self.append_sentence(contour, self.generate_arc_idtf('->', False), obj, False) - - group.value = contour - result = contour - else: - link_idtf = self.generate_link_idtf() - self.link_contents[link_idtf] = group.value - group.value = '"file://%s"' % link_idtf - result = link_idtf - - self.buildFormatRelation(group.value, u'txt') - - return result - - def processSetGroup(self, group): - """Process set - """ - idtf = self.resolve_identifier(group) - for item in group.items: - attributes = item[0] - object = item[1] - - self.parse_tree(object) - arc_idtf = self.generate_arc_idtf('->') - self.append_sentence(idtf, arc_idtf, self.resolve_identifier(object), False) - - for attr in attributes: - attr_idtf = self.resolve_identifier(attr) - self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) - - - def processOSetGroup(self, group): - """Process ordered tree - """ - idtf = self.resolve_identifier(group) - item_count = 0 - for item in group.items: - attributes = item[0] - object = item[1] - item_count += 1 - - object_idtf = self.resolve_identifier(object) - self.parse_tree(object) - arc_idtf = self.generate_arc_idtf('->') - self.append_sentence(idtf, arc_idtf, object_idtf, False) - # add order attribute - self.append_sentence("%d_" % item_count, self.generate_arc_idtf('->'), arc_idtf, False) - - for attr in attributes: - attr_idtf = self.resolve_identifier(attr) - self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), object_idtf, False) - - def processSentenceGroup(self, group): - """Process sentence for scs-levels 2-6 - """ - subject = group.subject - predicate = group.predicate - attributes = group.attrs - objects = group.object - - self.parse_tree(subject) - subject_idtf = self.resolve_identifier(subject) - for obj in objects: - # process object - self.parse_tree(obj) - # resolve object identifier - obj_idtf = self.resolve_identifier(obj) - - - if predicate == u'=': - self.append_synonyms(obj_idtf, subject_idtf) - else: - # connect subject with object - arc_idtf = self.generate_arc_idtf(predicate) - self.append_sentence(subject_idtf, arc_idtf, obj_idtf, self.check_predicate_mirror(predicate)) - - # connect attributes - for attr in attributes: - self.parse_tree(attr) - attr_idtf = self.resolve_identifier(attr) - self.append_sentence(attr_idtf, self.generate_arc_idtf('->'), arc_idtf, False) - - def processSynonymGroup(self, group): - pass - - - def get_string_value(self, element): - """Returns string representation of specified element - from parser results - """ - pass - - def parse_skip(self, group): - pass - - def parse_simple_sentence(self, group): - pass - - - # ----------------------------------------- - def parse_tree(self, tree): - """Parse results tree - """ - - if isinstance(tree, ParseResults): - for item in tree: - self.parse_tree(item) - else: - self.group_processors[tree.__class__](tree) - - - def parse_string(self, data): - """Parse specified string - """ - try: - result = scs_parser.syntax().parseString(data.decode('utf-8'), parseAll = True) - self.parse_tree(result) - - # process synonyms - new_triples = [] - for subj, predicate, obj in self.triples: - new_triples.append((self.resolve_synonym(subj), predicate, self.resolve_synonym(obj))) - self.triples = new_triples - - - except ParseException, err: - print err.line - print " "*(err.column-1) + "^" - print err - - def parse_directory(self, path): - """Parse specified directory - """ - self.process_dir = path - for root, dirs, files in os.walk(path): - #print root - for f in files: - - # skip none scs files - base, ext = os.path.splitext(f) - if ext != '.scs': - continue - - file_path = os.path.join(root, f) - self.process_file = file_path - - self.comments[len(self.triples)] = file_path - - # parse file - print "Parse %s" % file_path - input = open(file_path, "r") - self.parse_string(input.read().decode("utf-8")) - input.close() - - def write_to_fs(self, path): - """Writes converted data into specified directory - """ - if os.path.exists(path): - shutil.rmtree(path) - - os.makedirs(path) - - count = 0 - triples = [] - triples.extend(self.triples) - triples.extend(self.metaTriples) - with codecs.open(os.path.join(path, "data.scs"), "w", "utf-8") as output: - for t in triples: - - if self.comments.has_key(count): - output.write('\n/* --- %s --- */\n' % self.comments[count]) - - output.write("%s | %s | %s;;\n" % t) - count += 1 - output.close() - - # write contents - os.makedirs(os.path.join(path, "data")) - for num, data in self.link_contents.items(): - f = open(os.path.join(path, str(num)), "w") - f.write(data) - f.close() - - # copy contents - for num, src_path in self.link_copy_contents.items(): - shutil.copyfile(src_path, os.path.join(path, str(num))) - -if __name__ == "__main__": - - print "Default encoding: %s" % sys.getdefaultencoding() - - if len(sys.argv) < 3: - print "Usage: python converter.py " - sys.exit(0) - - converter = Converter() - converter.parse_directory(sys.argv[1]) - print "Write output..." - converter.write_to_fs(sys.argv[2]) diff --git a/tools/scs/scs_parser.py b/tools/scs/scs_parser.py deleted file mode 100644 index d0a1a1e9..00000000 --- a/tools/scs/scs_parser.py +++ /dev/null @@ -1,462 +0,0 @@ -# -*- coding: utf-8 -*- - -""" ------------------------------------------------------------------------------ -This source file is part of OSTIS (Open Semantic Technology for Intelligent Systems) -For the latest info, see http://www.ostis.net - -Copyright (c) 2012 OSTIS - -OSTIS is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -OSTIS 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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with OSTIS. If not, see . ------------------------------------------------------------------------------ -""" - -import re, sys, traceback -from pyparsing import Word, Literal, Forward, Regex, Group, ZeroOrMore, SkipTo, ParserElement -from pyparsing import OneOrMore, srange, Keyword, QuotedString, ParseResults, Optional, cStyleComment - -#ParserElement.enablePackrat() - -reKeyword = r'/!\*\s*keyword:\s*([a-zA-Z0-9_]+)\s*\*/' - -# parse results cache -maxCachedResults = 10 -cacheMap = {} -cacheQueue = [] - -connectors = [u'<>', - u'>', - u'<', - u'..>', - u'<..', - u'->', - u'<-', - u'<=>', - u'=>', - u'<=', - u'-|>', - u'<|-', - u'-/>', - u'', - u'<~', - u'~|>', - u'<|~', - u'~/>', - u' 1: - self.internal = tokens[0][1] - - def __call__(self, tokens): - return IdentifierGroup(tokens) - - def __str__(self): - if self.internal is not None: - return str(self.identifier) + str(self.internal) - - return str(self.identifier) - - def __eq__(self, other): - if not isinstance(other, IdentifierGroup): - return False - - if self.identifier != other.identifier: - return False - - return self.internal == other.internal - -class SimpleIdentifierGroup(BaseGroup): - """Class that represents simple identifier - """ - def __init__(self, tokens): - self.value = tokens[0] - - def __call__(self, tokens): - return SimpleIdentifierGroup(tokens) - - def __str__(self): - return str(self.value) - - def __eq__(self, other): - if not isinstance(other, SimpleIdentifierGroup): - return False - - return self.value == other.value - -class UrlGroup(BaseGroup): - - def __init__(self, tokens): - self.value = tokens[0] - - def __call__(self, tokens): - return UrlGroup(tokens) - - def __str__(self): - return str(self.value) - - def __eq__(self, other): - if not isinstance(other, SimpleIdentifierGroup): - return False - - return self.value == other.value - -class AliasGroup(BaseGroup): - """Class that represent identifiers, that hasn't name (names doesn't translate into memory) - """ - def __init__(self, tokens): - pass - - def __call__(self, tokens): - return AliasGroup(tokens) - - def __str__(self): - return 'ooo' - - def __eq__(self, other): - return False # they always doesn't equivalent - -class ContentGroup(BaseGroup): - """Class that represent content information - """ - def __init__(self, tokens): - self.value = tokens[0] - - def __call__(self, tokens): - return ContentGroup(tokens) - - def __str__(self): - return str(self.value) - - def __eq__(self, other): - if not isinstance(other, ContentGroup): - return False - - return self.value == other.value - -class SetGroup(BaseGroup): - """Class that represents set identifier - """ - def __init__(self, tokens): - self.items = tokens[0] - self.par = ('{', '}') - - def __call__(self, tokens): - return SetGroup(tokens) - - def __str__(self): - - res = self.par[0] - first = True - # TODO: sort items by alphabet - for item in self.items: - if not first: - res += ', ' - else: - first = False - res += str(item) - - res += self.par[1] - return res - - def __eq__(self, other): - # TODO: implement comparsion - return False - - -class OSetGroup(SetGroup): - - def __init__(self, tokens): - SetGroup.__init__(self, tokens) - self.par = ('<', '>') - - def __call__(self, tokens): - return OSetGroup(tokens) - - def __eq__(self, other): - - if len(self.items) != len(other.items): - return False - - for idx in xrange(len(self.items)): - if self.items[idx] != other.items[idx]: - return False - - return True - - def __str__(self): - - res = self.par[0] - first = True - # TODO: sort items by alphabet - for item in self.items: - if not first: - res += ', ' - else: - first = False - res += str(item) - - res += self.par[1] - return res - -class TripleGroup(BaseGroup): - """Class that represent triple - """ - def __init__(self, tokens): - self.subject = tokens[0][0] - self.predicate = tokens[0][1] - self.object = tokens[0][2] - - def __call__(self, tokens): - return TripleGroup(tokens) - - def __str__(self): - return "( " + str(self.subject) + " | " + str(self.predicate) + " | " + str(self.object) + " )" - - def __eq__(self, other): - return self.subject == other.subkect and self.predicate == other.predicate and self.object == other.object - - -class SimpleSentenceGroup(TripleGroup): - """Class that represents sentence of scs-code level 1 - """ - def __str__(self): - return TripleGroup.__str__(self)[2 : -2] - - def __eq__(self, other): - # TODO: implement comparsion - return False - -class SynonymGroup(BaseGroup): - - def __init__(self, tokens): - self.first = tokens[0][0] - self.second = tokens[0][1] - - def __call__(self, tokens): - return SynonymGroup(tokens) - - def __str__(self): - return "%s = %s" % (str(self.first), str(self.second)) - - def __eq__(self, other): - # TODO: implement comparsion - return False - -class SentenceGroup(BaseGroup): - """Class that represents sentence - """ - def __init__(self, tokens): - self.subject = tokens[0][0] - self.predicate = tokens[0][1] - self.attrs = tokens[0][2] - self.object = tokens[0][3] - - def __call__(self, tokens): - return SentenceGroup(tokens) - - def __str__(self): - return "sentence: " + str(self.subject) + " " + str(self.predicate) + " " + str(self.attrs) + " " + str(self.object) - - def __eq__(self, other): - # TODO: implement comparsion - return False - -class IdtfWithIntGroup(BaseGroup): - - def __init__(self, tokens): - self.idtf = tokens[0][0] - self.internal = None - if len(tokens[0]) > 1: - self.internal = tokens[0][1] - - def __call__(self, tokens): - return IdtfWithIntGroup(tokens) - - def __str__(self): - return "%s" % str(self.idtf) - - def __eq__(self, other): - # TODO: implement comparsion - return False - -class InternalGroup(BaseGroup): - - def __init__(self, tokens): - self.predicate = tokens[0][0] - self.attrs = tokens[0][1] - self.object = tokens[0][2] - - def __call__(self, tokens): - return InternalGroup(tokens) - - def __str__(self): - return '(* ' + str(self.predicate) + str(self.attrs) + str(self.object) + ' *)' - -class InternalListGroup(BaseGroup): - - def __init__(self, tokens): - self.sentences = tokens[0] - - def __call__(self, tokens): - return InternalListGroup(tokens) - - def __str__(self): - return str(self.sentences) - -def syntax(): - - syntax = None - - name = Word(srange(u"[\.a-zA-Z0-9_#]"), srange(u"[\.a-zA-Z0-9_#]")).setParseAction(SimpleIdentifierGroup).setName("Name") - url = QuotedString(quoteChar='"', unquoteResults=False).setParseAction(UrlGroup).setName("Url") - simpleIdtf = name ^ url - - tripleSep = Literal(u'|').suppress() - attrSep = Literal(u':').suppress() - objSep = Literal(u';').suppress() - sentSep = Literal(u';;').suppress() - synSep = Literal(u'=').suppress() - lpar = Literal(u'(').suppress() - rpar = Literal(u')').suppress() - aliasNoName = Literal(u'***').suppress() - lpar_set = Literal(u'{').suppress() - rpar_set = Literal(u'}').suppress() - lpar_oset = Literal(u'<').suppress() - rpar_oset = Literal(u'>').suppress() - lpar_trf = Literal(u'[').suppress() - rpar_trf = Literal(u']').suppress() - lpar_int = Literal(u'(*').suppress() - rpar_int = Literal(u'*)').suppress() - plus = Literal(u'+').suppress() - - # comments - comment_keyword = Regex(reKeyword).setParseAction(KeywordGroup) - comment = comment_keyword - - # level 1 sentence - sentence_lv1 = Group(simpleIdtf + tripleSep + simpleIdtf + tripleSep + simpleIdtf).setParseAction(SimpleSentenceGroup).setName("SimpleSentence") - - - # other levels sentence - connector = None - for c in connectors: - if c == connectors[0]: - connector = Literal(c) ^ Literal(u'_' + c) - else: - connector = connector ^ Literal(c) ^ Literal(u'_' + c) - - # identifiers - idtf = Forward() - internal = Forward() - - attrsList = Group(ZeroOrMore(simpleIdtf + attrSep)) - - # internal sentence - idtfWithInt = Group(idtf + Optional(internal)).setParseAction(IdtfWithIntGroup).setName("IdtfWithIntGroup") - objectList = Group(idtfWithInt + ZeroOrMore(objSep + idtfWithInt)) - intSentence = Group(connector + Optional(attrsList) + objectList).setParseAction(InternalGroup).setName("InternalSentence") - - intSentenceList = Group(lpar_int + OneOrMore(intSentence + sentSep) + rpar_int).setParseAction(InternalListGroup).setName("InternalSentenceGroup") - - internal << intSentenceList - - content = QuotedString(quoteChar=u'[', endQuoteChar=u']', unquoteResults=True, multiline=True, escChar=u'\\').setParseAction(ContentGroup).setName("Content") - triple = Group(lpar + idtf + connector + idtf + rpar).setParseAction(TripleGroup).setName("Triple") - alias = Group(aliasNoName).setParseAction(AliasGroup).setName("Alias") - - setIdtf = Group(lpar_set + ZeroOrMore(Group(Optional(attrsList) + idtfWithInt) + objSep) + Group(Optional(attrsList) + idtfWithInt) + rpar_set).setParseAction(SetGroup).setName("Set") - osetIdtf = Group(lpar_oset + ZeroOrMore(Group(Optional(attrsList) + idtfWithInt) + objSep) + Group(Optional(attrsList) + idtfWithInt) + rpar_oset).setParseAction(OSetGroup).setName("OSet") - - anyIdtf = simpleIdtf ^ content ^ triple ^ setIdtf ^ osetIdtf ^ alias - idtf << anyIdtf - - #sentence_synonym = Group(idtf + synSep + idtf).setParseAction(SynonymGroup) - sentence_lv23456 = Group(idtf + connector + Optional(attrsList) + objectList).setParseAction(SentenceGroup).setName("Sentence") - - sentence = (sentence_lv1 ^ sentence_lv23456)# ^ sentence_synonym) - syntax = ZeroOrMore(Group(sentence + sentSep) ^ comment) - - syntax.ignore(cStyleComment) - - #syntax.setDebug() - - return syntax - -def parse(path): - """Parse scs file with specified \p path - """ - data = None - try: - f = open(path, 'r') - data = f.read().decode("utf-8") - f.close() - - fields = syntax().parseString(data) - - except: - print 'Error to parse "%s" file"' % path - print "Error:", sys.exc_info()[0] - traceback.print_exc(file=sys.stdout) - return None - - return fields - - diff --git a/tools/scs/tests/image.png b/tools/scs/tests/image.png deleted file mode 100644 index 7c5adb541710a7fc4529567117bf79f1a9b0e168..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24361 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 incl2;; -[* debug -> a;; *] <- debug_contour;; diff --git a/tools/scs/tests/test.scs b/tools/scs/tests/test.scs deleted file mode 100644 index e9cb2fd2..00000000 --- a/tools/scs/tests/test.scs +++ /dev/null @@ -1,48 +0,0 @@ -1 | test | 667;; -5 -> 6: 7: tr; ty;; -6 <- 8: re; gg; hj;; - -file | rac | "file://image.png";; -set -> attr_set: { sum: 1; result: 2; 3};; -set ~> < attr1: 1; attr2: 2; attr3: 3>;; - -el1 _<=> el2;; - -file => nrel_system_identifier: ["fs_file"] (* <- string; russian;; <~ opened;;*);; - -_contour -> [* - - a -> b;; - v -> c (* <- d;; *);; - .cnt -> g;; -*];; - -"file://image.png" <- format_png;; - -contour = [* test -> y;; *] (* <- x;; *);; -contour <- y;; - - - - -Partition_SCs_code <= nrel_decomposition: - { - Partition_SCs_code_1_level; - Partition_SCs_code_2_level; - Partition_SCs_code_3_level; - Partition_SCs_code_4_level; - Partition_SCs_code_5_level; - Partition_SCs_code_6_level; - Partition_SCs_code_7_level; - Partition_scs_divider - };; - - "file://image.png" = y;; - - Partition_SCs_code_1_level = - [* x -> y;; *];; - - - y -> [*^"file://incl.scsi"*];; - -const_arc -> (v -> (g <= n)) (* <- tested;; *);; From d7e8f745b97fcbc4a628cd3b6967773481eebafc Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Sun, 30 Mar 2014 01:18:49 +0300 Subject: [PATCH 33/84] done issue #1 --- config/sc-web.ini | 27 +++++++++++++++++++++ config/settings_local.py | 36 ++++++++++++++++++++++++++++ scripts/build_kb.sh | 4 ++++ scripts/prepare.sh | 52 ++++++++++++++++++++++++++++++++++++++++ scripts/restart_sctp.sh | 2 ++ scripts/run_scweb.sh | 1 + 6 files changed, 122 insertions(+) create mode 100644 config/sc-web.ini create mode 100644 config/settings_local.py create mode 100755 scripts/build_kb.sh create mode 100755 scripts/prepare.sh create mode 100755 scripts/restart_sctp.sh create mode 100755 scripts/run_scweb.sh diff --git a/config/sc-web.ini b/config/sc-web.ini new file mode 100644 index 00000000..f1f64078 --- /dev/null +++ b/config/sc-web.ini @@ -0,0 +1,27 @@ +[Network] +Port = 55770 + +[Repo] +Path = ../repo.bin + +[Extensions] +Directory = ../sc-machine/bin/extensions + +[Stat] +UpdatePeriod = 1800 +Path = ../sctp_stat + + +##### sc-memory +[memory] +max_loaded_segments = 10 +save_period = 30 + +[redis] +host = 127.0.0.1 +port = 6379 +timeout = 1500 + +[filememory] +engine = redis + diff --git a/config/settings_local.py b/config/settings_local.py new file mode 100644 index 00000000..c60f805c --- /dev/null +++ b/config/settings_local.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- + +DEBUG = True +API_DEBUG = DEBUG +TEMPLATE_DEBUG = DEBUG + +#SCTP_PORT = 56789 +SCTP_HOST = 'localhost' # 'ims.ostis.net' + +REPO_EDIT_TIMEOUT = 60 # seconds + +#DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.sqlite3', +# 'NAME': 'data.db', +# 'USER': '', +# 'PASSWORD': '', +# 'HOST': '', +# 'PORT': '', +# } +#} + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'sc_web', + 'USER': 'root', + 'PASSWORD': 'root', + 'HOST': '', + 'PORT': '', + } +} + +SITE_URL = 'http://localhost:8000' + + diff --git a/scripts/build_kb.sh b/scripts/build_kb.sh new file mode 100755 index 00000000..e0a6cd26 --- /dev/null +++ b/scripts/build_kb.sh @@ -0,0 +1,4 @@ +export LD_LIBRARY_PATH=../sc-machine/bin +mkdir repo.bin +../sc-machine/bin/sc-builder -f -c -e ../sc-machine/bin/extensions -s ../config/sc-web.ini -i ../kb.sources -o ../repo.bin + diff --git a/scripts/prepare.sh b/scripts/prepare.sh new file mode 100755 index 00000000..7c5748a7 --- /dev/null +++ b/scripts/prepare.sh @@ -0,0 +1,52 @@ +echo -en '\E[47;31m'"\033[1mScript to prepare ostis tools started\033[0m\n" + + + +clone_project() +{ + echo -en '\E[0;32m'"\033[1mClone $2\033[0m\n" + + if [ ! -d "../$2" ]; then + git clone $1 ../$2 + fi + cd ../$2 + git checkout $3 + cd - +} + +clone_project git@github.com:deniskoronchik/sc-machine.git sc-machine v0.1.0 +clone_project git@github.com:deniskoronchik/ims.ostis.kb.git kb.sources v0.1.0 +clone_project git@github.com:deniskoronchik/sc-web.git sc_web v0.1.0 + +echo -en '\E[0;32m'"\033[1mInstall dependencies for sc-machine\033[0m\n" +sudo ../sc-machine/scripts/install_deps_ubuntu.sh +choice="" + +while [ "$choice" != "1" -a "$choice" != "2" ] +do + echo + echo "Do you want to setup redis >= 2.8 ?" + echo "1) yes" + echo "2) no" + echo + + read choice + + case $choice in + '1') ../sc-machine/scripts/install_redis_ubuntu.sh;; + '2') echo "Skip redis install";; + *) echo "try again!";; + esac +done + +echo -en '\E[0;32m'"\033[1mBuild sc-machine\033[0m\n" +cd ../sc-machine/scripts +./make_all.sh +cd - + +echo -en '\E[0;32m'"\033[1mInstall dependencies for sc-web\033[0m\n" +cd ../sc_web/scripts +./install_deps_ubuntu.sh +./prepare.sh +cd - +cp ../config/settings_local.py ../sc_web/sc_web/sc_web/ diff --git a/scripts/restart_sctp.sh b/scripts/restart_sctp.sh new file mode 100755 index 00000000..735d04a0 --- /dev/null +++ b/scripts/restart_sctp.sh @@ -0,0 +1,2 @@ +./build_kb.sh +../sc-machine/bin/sctp-server ../config/sc-web.ini diff --git a/scripts/run_scweb.sh b/scripts/run_scweb.sh new file mode 100755 index 00000000..66e08281 --- /dev/null +++ b/scripts/run_scweb.sh @@ -0,0 +1 @@ +python ../sc_web/sc_web/manage.py runserver From 52b6909d30ee9c38c30083dc792646a004a7b482 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Wed, 30 Apr 2014 10:00:14 +0300 Subject: [PATCH 34/84] fix url's in prepare.sh script --- scripts/prepare.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index 7c5748a7..ae7ad289 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -14,9 +14,9 @@ clone_project() cd - } -clone_project git@github.com:deniskoronchik/sc-machine.git sc-machine v0.1.0 -clone_project git@github.com:deniskoronchik/ims.ostis.kb.git kb.sources v0.1.0 -clone_project git@github.com:deniskoronchik/sc-web.git sc_web v0.1.0 +clone_project https://github.com/deniskoronchik/sc-machine.git sc-machine v0.1.0 +clone_project https://github.com/deniskoronchik/ims.ostis.kb.git kb.sources v0.1.0 +clone_project https://github.com/deniskoronchik/sc-web.git sc_web v0.1.0 echo -en '\E[0;32m'"\033[1mInstall dependencies for sc-machine\033[0m\n" sudo ../sc-machine/scripts/install_deps_ubuntu.sh From 5d85a2302e585c204040776fc8998e9cd5604888 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Wed, 30 Apr 2014 10:10:11 +0300 Subject: [PATCH 35/84] fix order of sc-web install --- scripts/prepare.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index ae7ad289..1e26ad05 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -47,6 +47,7 @@ cd - echo -en '\E[0;32m'"\033[1mInstall dependencies for sc-web\033[0m\n" cd ../sc_web/scripts ./install_deps_ubuntu.sh -./prepare.sh cd - cp ../config/settings_local.py ../sc_web/sc_web/sc_web/ +cd ../sc_web/scripts +./prepare.sh From 31ad49cabdd2f70b8fc4bddf1a40da22d74cac95 Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Wed, 30 Apr 2014 11:01:18 +0300 Subject: [PATCH 36/84] work on preprare script --- scripts/build_kb.sh | 2 +- scripts/prepare.sh | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/build_kb.sh b/scripts/build_kb.sh index e0a6cd26..afa6fa2c 100755 --- a/scripts/build_kb.sh +++ b/scripts/build_kb.sh @@ -1,4 +1,4 @@ export LD_LIBRARY_PATH=../sc-machine/bin -mkdir repo.bin +mkdir ../repo.bin ../sc-machine/bin/sc-builder -f -c -e ../sc-machine/bin/extensions -s ../config/sc-web.ini -i ../kb.sources -o ../repo.bin diff --git a/scripts/prepare.sh b/scripts/prepare.sh index 1e26ad05..6048fc9d 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -51,3 +51,6 @@ cd - cp ../config/settings_local.py ../sc_web/sc_web/sc_web/ cd ../sc_web/scripts ./prepare.sh +cd - + + From 7443ddb8e1353c8e12830718c85cbe52a7b561fc Mon Sep 17 00:00:00 2001 From: Denis Koronchik Date: Wed, 30 Apr 2014 20:51:02 +0300 Subject: [PATCH 37/84] add update of components --- scripts/prepare.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index 6048fc9d..37762a98 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -4,10 +4,15 @@ echo -en '\E[47;31m'"\033[1mScript to prepare ostis tools started\033[0m\n" clone_project() { - echo -en '\E[0;32m'"\033[1mClone $2\033[0m\n" if [ ! -d "../$2" ]; then + echo -en '\E[0;32m'"\033[1mClone $2\033[0m\n" git clone $1 ../$2 + else + echo -en '\E[0;32m'"\033[1mUpdate $2\033[0m\n" + cd ../$2 + git pull + cd - fi cd ../$2 git checkout $3 From bdf271cf94242d76727177d6d1fa2767257568a3 Mon Sep 17 00:00:00 2001 From: ShunkevichDV Date: Sat, 24 Jan 2015 16:51:37 +0300 Subject: [PATCH 38/84] Version 0.2.0 --- config/sc-web.ini | 17 ++------ config/server.conf | 3 ++ config/settings_local.py | 36 ---------------- scripts/build_kb.sh | 11 ++++- scripts/clean.sh | 6 +++ scripts/prepare.sh | 91 +++++++++++++++++++++------------------- scripts/run_sctp.sh | 4 ++ scripts/run_scweb.sh | 6 ++- 8 files changed, 80 insertions(+), 94 deletions(-) create mode 100644 config/server.conf delete mode 100644 config/settings_local.py create mode 100755 scripts/clean.sh create mode 100755 scripts/run_sctp.sh diff --git a/config/sc-web.ini b/config/sc-web.ini index f1f64078..041ee57e 100644 --- a/config/sc-web.ini +++ b/config/sc-web.ini @@ -1,26 +1,17 @@ [Network] Port = 55770 - [Repo] -Path = ../repo.bin - +Path = ../kb.bin [Extensions] Directory = ../sc-machine/bin/extensions - [Stat] UpdatePeriod = 1800 -Path = ../sctp_stat - +Path = /tmp/sctp_stat ##### sc-memory [memory] -max_loaded_segments = 10 -save_period = 30 - -[redis] -host = 127.0.0.1 -port = 6379 -timeout = 1500 +max_loaded_segments = 100 +save_period = 3600 [filememory] engine = redis diff --git a/config/server.conf b/config/server.conf new file mode 100644 index 00000000..861812ac --- /dev/null +++ b/config/server.conf @@ -0,0 +1,3 @@ + +static_path = "../client/static" +templates_path = "../client/templates" diff --git a/config/settings_local.py b/config/settings_local.py deleted file mode 100644 index c60f805c..00000000 --- a/config/settings_local.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- - -DEBUG = True -API_DEBUG = DEBUG -TEMPLATE_DEBUG = DEBUG - -#SCTP_PORT = 56789 -SCTP_HOST = 'localhost' # 'ims.ostis.net' - -REPO_EDIT_TIMEOUT = 60 # seconds - -#DATABASES = { -# 'default': { -# 'ENGINE': 'django.db.backends.sqlite3', -# 'NAME': 'data.db', -# 'USER': '', -# 'PASSWORD': '', -# 'HOST': '', -# 'PORT': '', -# } -#} - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'sc_web', - 'USER': 'root', - 'PASSWORD': 'root', - 'HOST': '', - 'PORT': '', - } -} - -SITE_URL = 'http://localhost:8000' - - diff --git a/scripts/build_kb.sh b/scripts/build_kb.sh index afa6fa2c..9d7a4110 100755 --- a/scripts/build_kb.sh +++ b/scripts/build_kb.sh @@ -1,4 +1,11 @@ +#!/bin/bash + export LD_LIBRARY_PATH=../sc-machine/bin -mkdir ../repo.bin -../sc-machine/bin/sc-builder -f -c -e ../sc-machine/bin/extensions -s ../config/sc-web.ini -i ../kb.sources -o ../repo.bin +if [ ! -d "../kb.bin" ]; then + mkdir ../kb.bin +fi + +cd .. +sc-machine/bin/sc-builder -f -c -i repo.path -o kb.bin -s config/sc-web.ini -e sc-machine/bin/extensions + diff --git a/scripts/clean.sh b/scripts/clean.sh new file mode 100755 index 00000000..7522d108 --- /dev/null +++ b/scripts/clean.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +rm -rf ../sc-machine +rm -rf ../sc-web +rm -rf ../kb.bin +rm -rf ../geometry.drawings diff --git a/scripts/prepare.sh b/scripts/prepare.sh index 37762a98..f416b41b 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -1,61 +1,68 @@ -echo -en '\E[47;31m'"\033[1mScript to prepare ostis tools started\033[0m\n" +#!/bin/bash +red="\e[1;31m" # Red B +blue="\e[1;34m" # Blue B +green="\e[0;32m" +bwhite="\e[47m" # white background -clone_project() -{ +rst="\e[0m" # Text reset - if [ ! -d "../$2" ]; then - echo -en '\E[0;32m'"\033[1mClone $2\033[0m\n" - git clone $1 ../$2 - else - echo -en '\E[0;32m'"\033[1mUpdate $2\033[0m\n" - cd ../$2 - git pull - cd - - fi - cd ../$2 - git checkout $3 - cd - +st=1 + +stage() +{ + echo -en "$green[$st] "$blue"$1...$rst\n" + let "st += 1" } -clone_project https://github.com/deniskoronchik/sc-machine.git sc-machine v0.1.0 -clone_project https://github.com/deniskoronchik/ims.ostis.kb.git kb.sources v0.1.0 -clone_project https://github.com/deniskoronchik/sc-web.git sc_web v0.1.0 +clone_project() +{ + if [ ! -d "../$2" ]; then + echo -en $green"Clone $2$rst\n" + git clone $1 ../$2 + cd ../$2 + git checkout $3 + cd - + else + echo -en "You can update "$green"$2"$rst" manualy$rst\n" + fi +} -echo -en '\E[0;32m'"\033[1mInstall dependencies for sc-machine\033[0m\n" -sudo ../sc-machine/scripts/install_deps_ubuntu.sh -choice="" +stage "Clone projects" -while [ "$choice" != "1" -a "$choice" != "2" ] -do - echo - echo "Do you want to setup redis >= 2.8 ?" - echo "1) yes" - echo "2) no" - echo +clone_project https://github.com/deniskoronchik/sc-machine.git sc-machine master +clone_project https://github.com/deniskoronchik/sc-web.git sc-web master +clone_project https://github.com/deniskoronchik/ims.ostis.kb.git ims.ostis.kb master - read choice +stage "Prepare projects" - case $choice in - '1') ../sc-machine/scripts/install_redis_ubuntu.sh;; - '2') echo "Skip redis install";; - *) echo "try again!";; - esac -done +prepare() +{ + echo -en $green$1$rst"\n" +} -echo -en '\E[0;32m'"\033[1mBuild sc-machine\033[0m\n" +prepare "sc-machine" cd ../sc-machine/scripts +./install_deps_ubuntu.sh + +if [ ! -d "redis-2.8.4" ]; then +./install_redis_ubuntu.sh +fi + +./clean_all.sh ./make_all.sh cd - -echo -en '\E[0;32m'"\033[1mInstall dependencies for sc-web\033[0m\n" -cd ../sc_web/scripts +prepare "sc-web" +cd ../sc-web/scripts ./install_deps_ubuntu.sh +./prepare_js.sh +python build_components.py -i -a cd - -cp ../config/settings_local.py ../sc_web/sc_web/sc_web/ -cd ../sc_web/scripts -./prepare.sh -cd - +echo -en $green"Copy server.conf"$rst"\n" +cp -f ../config/server.conf ../sc-web/server/ +stage "Build knowledge base" +./build_kb.sh diff --git a/scripts/run_sctp.sh b/scripts/run_sctp.sh new file mode 100755 index 00000000..b882229b --- /dev/null +++ b/scripts/run_sctp.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +export LD_LIBRARY_PATH=../sc-machine/bin +../sc-machine/bin/sctp-server ../config/sc-web.ini diff --git a/scripts/run_scweb.sh b/scripts/run_scweb.sh index 66e08281..bac81ed5 100755 --- a/scripts/run_scweb.sh +++ b/scripts/run_scweb.sh @@ -1 +1,5 @@ -python ../sc_web/sc_web/manage.py runserver +#!/bin/bash + +cd ../sc-web/server/ +python app.py +cd - From a5e6049ef1353f86d0987d6f520c97ffeb5d5ab4 Mon Sep 17 00:00:00 2001 From: ShunkevichDV Date: Mon, 26 Jan 2015 13:55:03 +0300 Subject: [PATCH 39/84] Build kb config added --- repo.path | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 repo.path diff --git a/repo.path b/repo.path new file mode 100644 index 00000000..73f1f610 --- /dev/null +++ b/repo.path @@ -0,0 +1,5 @@ +# common +ims.ostis.kb + +#components +sc-web/components/scg/kb From 6543e46af1c71efab77d0a215746eab68ac4031c Mon Sep 17 00:00:00 2001 From: ShunkevichDV Date: Thu, 26 Feb 2015 11:56:15 +0300 Subject: [PATCH 40/84] Component approach used, scripts updated --- .gitignore | 9 +++++++++ kb/menu/ui_main_menu.scs | 11 +++++++++++ kb/menu/ui_menu_na_keynodes.scs | 5 +++++ repo.path | 7 +++++-- scripts/build_kb.sh | 2 ++ scripts/copy_ims_kb.sh | 25 +++++++++++++++++++++++++ scripts/prepare.sh | 9 ++++++++- 7 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 .gitignore create mode 100644 kb/menu/ui_main_menu.scs create mode 100644 kb/menu/ui_menu_na_keynodes.scs create mode 100755 scripts/copy_ims_kb.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..091e06d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +kb.bin +sc-machine +scp-machine +sc-web +ims.ostis.kb +ims.ostis.kb_copy +geometry.drawings + +*.*~ diff --git a/kb/menu/ui_main_menu.scs b/kb/menu/ui_main_menu.scs new file mode 100644 index 00000000..66a621e3 --- /dev/null +++ b/kb/menu/ui_main_menu.scs @@ -0,0 +1,11 @@ +ui_main_menu <- sc_node_not_relation;; + +ui_main_menu => nrel_main_idtf: [Главное меню] (* <- lang_ru;; *);; + +ui_main_menu <= nrel_ui_commands_decomposition: +{ + ui_menu_na_view_kb_extended; + ui_menu_na_keynodes +};; + + diff --git a/kb/menu/ui_menu_na_keynodes.scs b/kb/menu/ui_menu_na_keynodes.scs new file mode 100644 index 00000000..32950aef --- /dev/null +++ b/kb/menu/ui_menu_na_keynodes.scs @@ -0,0 +1,5 @@ +ui_menu_na_keynodes +<- ui_user_command_class_noatom; +=> nrel_main_idtf: + [Ключевые узлы навигации] (* <- lang_ru;; *); + [Keynodes for navigation] (* <- lang_en;; *);; diff --git a/repo.path b/repo.path index 73f1f610..97ceeeb8 100644 --- a/repo.path +++ b/repo.path @@ -1,5 +1,8 @@ # common -ims.ostis.kb +ims.ostis.kb_copy -#components +# private +kb + +# components sc-web/components/scg/kb diff --git a/scripts/build_kb.sh b/scripts/build_kb.sh index 9d7a4110..75d80ab8 100755 --- a/scripts/build_kb.sh +++ b/scripts/build_kb.sh @@ -1,5 +1,7 @@ #!/bin/bash +./copy_ims_kb.sh + export LD_LIBRARY_PATH=../sc-machine/bin if [ ! -d "../kb.bin" ]; then mkdir ../kb.bin diff --git a/scripts/copy_ims_kb.sh b/scripts/copy_ims_kb.sh new file mode 100755 index 00000000..59e7b05e --- /dev/null +++ b/scripts/copy_ims_kb.sh @@ -0,0 +1,25 @@ +#!/bin/bash +export LD_LIBRARY_PATH=../sc-machine/bin +if [ ! -d "../ims.ostis.kb_copy" ]; then + mkdir ../ims.ostis.kb_copy +else + rm -rf ../ims.ostis.kb_copy/* +fi + +cd ../ + +cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_unificated_semantic_network_and_representation/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_unificated_models/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_basic_model_of_the_unified_processing_of_semantic_networks/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_library_OSTIS/section_library_of_reusable_components_interfaces/lib_ui_menu/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_library_OSTIS/section_library_of_reusable_components_processing_machinery_knowledge/lib_c_agents/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_library_OSTIS/section_library_of_reusable_components_processing_machinery_knowledge/lib_scp_agents/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_library_OSTIS/section_library_of_reusable_components_processing_machinery_knowledge/section_library_of_reusable_programs_for_sc_text_processing/lib_scp_program/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/to_check/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/ui/ ims.ostis.kb_copy/ +rm -rf ims.ostis.kb_copy/ui/menu + +cd - + + + diff --git a/scripts/prepare.sh b/scripts/prepare.sh index f416b41b..e68f405c 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -32,8 +32,9 @@ clone_project() stage "Clone projects" clone_project https://github.com/deniskoronchik/sc-machine.git sc-machine master +clone_project https://github.com/ShunkevichDV/scp-machine.git scp-machine master clone_project https://github.com/deniskoronchik/sc-web.git sc-web master -clone_project https://github.com/deniskoronchik/ims.ostis.kb.git ims.ostis.kb master +clone_project https://github.com/ShunkevichDV/ims.ostis.kb.git ims.ostis.kb master stage "Prepare projects" @@ -54,6 +55,12 @@ fi ./make_all.sh cd - +prepare "scp-machine" +cd ../scp-machine/scripts +./clean_all.sh +./make_all.sh +cd - + prepare "sc-web" cd ../sc-web/scripts ./install_deps_ubuntu.sh From 2d09da2df5071caf8d2e1bb13db22665afc472f1 Mon Sep 17 00:00:00 2001 From: ShunkevichDV Date: Thu, 26 Mar 2015 21:04:24 +0300 Subject: [PATCH 41/84] script fixed --- scripts/prepare.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index e68f405c..f6fea4b0 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -57,7 +57,6 @@ cd - prepare "scp-machine" cd ../scp-machine/scripts -./clean_all.sh ./make_all.sh cd - From 7cfbc5eacd9bcccb82513a77784e6fd4d686ca10 Mon Sep 17 00:00:00 2001 From: ShunkevichDV Date: Sun, 25 Oct 2015 19:06:54 +0300 Subject: [PATCH 42/84] IMS KB copying script changed --- scripts/copy_ims_kb.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/copy_ims_kb.sh b/scripts/copy_ims_kb.sh index 59e7b05e..820a856a 100755 --- a/scripts/copy_ims_kb.sh +++ b/scripts/copy_ims_kb.sh @@ -8,13 +8,13 @@ fi cd ../ -cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_unificated_semantic_network_and_representation/ ims.ostis.kb_copy/ -cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_unificated_models/ ims.ostis.kb_copy/ -cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_basic_model_of_the_unified_processing_of_semantic_networks/ ims.ostis.kb_copy/ -cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_library_OSTIS/section_library_of_reusable_components_interfaces/lib_ui_menu/ ims.ostis.kb_copy/ -cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_library_OSTIS/section_library_of_reusable_components_processing_machinery_knowledge/lib_c_agents/ ims.ostis.kb_copy/ -cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_library_OSTIS/section_library_of_reusable_components_processing_machinery_knowledge/lib_scp_agents/ ims.ostis.kb_copy/ -cp -a ims.ostis.kb/knowledge_base_IMS/doc_technology_ostis/section_library_OSTIS/section_library_of_reusable_components_processing_machinery_knowledge/section_library_of_reusable_programs_for_sc_text_processing/lib_scp_program/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/ims/doc_technology_ostis/semantic_network_represent/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/ims/doc_technology_ostis/unificated_models/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/ims/doc_technology_ostis/semantic_networks_processing/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/ims/doc_technology_ostis/library_OSTIS/components_interface/ui_menu/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/ims/doc_technology_ostis/library_OSTIS/components_kpm/lib_c_agents/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/ims/doc_technology_ostis/library_OSTIS/components_kpm/lib_scp_agents/ ims.ostis.kb_copy/ +cp -a ims.ostis.kb/ims/doc_technology_ostis/library_OSTIS/components_kpm/programs_for_sc_text_processing/scp_program/ ims.ostis.kb_copy/ cp -a ims.ostis.kb/to_check/ ims.ostis.kb_copy/ cp -a ims.ostis.kb/ui/ ims.ostis.kb_copy/ rm -rf ims.ostis.kb_copy/ui/menu From 312c346bbc01264e0b426d8e915648a810324173 Mon Sep 17 00:00:00 2001 From: ShunkevichDV Date: Fri, 13 Nov 2015 16:08:40 +0300 Subject: [PATCH 43/84] sc-machine version changed --- scripts/prepare.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index f6fea4b0..c307e1e8 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -31,7 +31,7 @@ clone_project() stage "Clone projects" -clone_project https://github.com/deniskoronchik/sc-machine.git sc-machine master +clone_project https://github.com/ShunkevichDV/sc-machine.git sc-machine master clone_project https://github.com/ShunkevichDV/scp-machine.git scp-machine master clone_project https://github.com/deniskoronchik/sc-web.git sc-web master clone_project https://github.com/ShunkevichDV/ims.ostis.kb.git ims.ostis.kb master From c02f537d74fa74b12659d02d73f4c2b731f90382 Mon Sep 17 00:00:00 2001 From: rusetski-k Date: Fri, 20 Nov 2015 01:29:38 +0300 Subject: [PATCH 44/84] Apt-get Redis instead of building it from source There is v2.8.4 in 14.04's repositories already --- scripts/prepare.sh | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index c307e1e8..723333e6 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -47,9 +47,7 @@ prepare "sc-machine" cd ../sc-machine/scripts ./install_deps_ubuntu.sh -if [ ! -d "redis-2.8.4" ]; then -./install_redis_ubuntu.sh -fi +sudo apt-get install redis-server ./clean_all.sh ./make_all.sh From cadefc6b87115d388e2bb7a7a82f6d50e66352c2 Mon Sep 17 00:00:00 2001 From: rusetski-k Date: Fri, 20 Nov 2015 01:43:49 +0300 Subject: [PATCH 45/84] Install python-dev required for numpy --- scripts/prepare.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index 723333e6..263e41d9 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -59,6 +59,7 @@ cd ../scp-machine/scripts cd - prepare "sc-web" +sudo apt-get install python-dev # required for numpy module cd ../sc-web/scripts ./install_deps_ubuntu.sh ./prepare_js.sh From 98f592a85712b3fd5ec4898f69ec50e5ad76855c Mon Sep 17 00:00:00 2001 From: ShunkevichDV Date: Thu, 26 Nov 2015 15:09:27 +0300 Subject: [PATCH 46/84] scp-machine removed --- scripts/prepare.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/prepare.sh b/scripts/prepare.sh index c307e1e8..8378d26b 100755 --- a/scripts/prepare.sh +++ b/scripts/prepare.sh @@ -32,7 +32,6 @@ clone_project() stage "Clone projects" clone_project https://github.com/ShunkevichDV/sc-machine.git sc-machine master -clone_project https://github.com/ShunkevichDV/scp-machine.git scp-machine master clone_project https://github.com/deniskoronchik/sc-web.git sc-web master clone_project https://github.com/ShunkevichDV/ims.ostis.kb.git ims.ostis.kb master @@ -55,11 +54,6 @@ fi ./make_all.sh cd - -prepare "scp-machine" -cd ../scp-machine/scripts -./make_all.sh -cd - - prepare "sc-web" cd ../sc-web/scripts ./install_deps_ubuntu.sh From 2d2b0cfef980230b171b4061b7c87b7c01244647 Mon Sep 17 00:00:00 2001 From: Kirill Rusetski Date: Tue, 1 Dec 2015 04:32:45 +0400 Subject: [PATCH 47/84] Build scripts for 64-bit Windows --- ...umpy-1.9.3+vanilla-cp27-none-win_amd64.whl | Bin 0 -> 3636725 bytes scripts/bootstrap-ostis-stage1.ps1 | 34 +++++++ scripts/bootstrap-ostis-stage2.ps1 | 83 ++++++++++++++++++ scripts/bootstrap-ostis-win64.cmd | 3 + scripts/build_kb.cmd | 5 ++ scripts/restart_sctp.cmd | 4 + scripts/run_sctp.cmd | 2 + scripts/run_scweb.cmd | 5 ++ 8 files changed, 136 insertions(+) create mode 100644 scripts/3rd-party/numpy-1.9.3+vanilla-cp27-none-win_amd64.whl create mode 100644 scripts/bootstrap-ostis-stage1.ps1 create mode 100644 scripts/bootstrap-ostis-stage2.ps1 create mode 100644 scripts/bootstrap-ostis-win64.cmd create mode 100644 scripts/build_kb.cmd create mode 100644 scripts/restart_sctp.cmd create mode 100644 scripts/run_sctp.cmd create mode 100644 scripts/run_scweb.cmd diff --git a/scripts/3rd-party/numpy-1.9.3+vanilla-cp27-none-win_amd64.whl b/scripts/3rd-party/numpy-1.9.3+vanilla-cp27-none-win_amd64.whl new file mode 100644 index 0000000000000000000000000000000000000000..eddb9048f16ded1282508ca9d559eb32f79cfe34 GIT binary patch literal 3636725 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