From 42dfa6cddc72fff2c6b48213f1f194ac7e0db55f Mon Sep 17 00:00:00 2001 From: strangeqargo <“strangeqargo@gmail.com”> Date: Thu, 5 May 2016 01:58:53 +0300 Subject: [PATCH] argument parsing, fail on parsing --- .gitignore | 1 + scripts/ddl2cpp | 144 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 97 insertions(+), 48 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..485dee64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/scripts/ddl2cpp b/scripts/ddl2cpp index c4d41584..7b354d63 100755 --- a/scripts/ddl2cpp +++ b/scripts/ddl2cpp @@ -31,40 +31,28 @@ import re import os import pprint +# error codes, we should refactor this later +ERROR_DATA_TYPE = 10 +ERROR_STRANGE_PARSING = 20 + from pyparsing import CaselessLiteral, Literal, SkipTo, restOfLine, oneOf, ZeroOrMore, Optional, Combine, Suppress, \ - WordStart, WordEnd, Word, alphas, alphanums, nums, QuotedString, nestedExpr, MatchFirst, OneOrMore, delimitedList, Or, Group - -def usage(): - print('Usage: ddl2cpp [-no-timestamp-warning] ') - -if len(sys.argv) not in (4,5): - usage() - sys.exit(1) - -firstPositional = 1 -timestampWarning = True -if len(sys.argv) == 5: - if sys.argv[1] == '-no-timestamp-warning': - firstPositional += 1 - timestampWarning = False - else: - usage() - sys.exit(1) - -pathToDdl = sys.argv[firstPositional] -pathToHeader = sys.argv[firstPositional + 1] + '.h' -namespace = sys.argv[firstPositional + 2] - -INCLUDE = 'sqlpp11' -NAMESPACE = 'sqlpp' + WordStart, WordEnd, Word, alphas, alphanums, nums, QuotedString, nestedExpr, MatchFirst, OneOrMore, delimitedList, Or, Group, ParseException # HELPERS + def get_include_guard_name(namespace, inputfile): val = re.sub("[^A-Za-z]+", "_", namespace + '_' + os.path.basename(inputfile)) return val.upper() + def repl_func(m): - if (m.group(1) == '_'): + if m.group(1) == '_': + return m.group(2).upper() + else: + return m.group(1) + m.group(2).upper() + +def repl_func_for_args(m): + if m.group(1) == '-': return m.group(2).upper() else: return m.group(1) + m.group(2).upper() @@ -72,10 +60,61 @@ def repl_func(m): def toClassName(s): return re.sub("(^|\s|[_0-9])(\S)", repl_func, s) + def toMemberName(s): return re.sub("(\s|_|[0-9])(\S)", repl_func, s) +def setArgumentBool(s, bool_value): + first_lower = lambda s: s[:1].lower() + s[1:] if s else '' # http://stackoverflow.com/a/3847369/5006740 + var_name = first_lower(re.sub("(\s|-|[0-9])(\S)", repl_func_for_args, s)) + globals()[var_name] = bool_value + + +def usage(): + print('Usage: ddl2cpp [-no-timestamp-warning] ') + +# ARGUMENT PARSING +if len(sys.argv) < (4): + usage() + sys.exit(20) + +firstPositional = 1 +timestampWarning = True +failOnParse = True + +optionalArgs = { + # if -some-key is present, it will set variable someKey to True + # if -no-some-key is present, it will set variable someKey to False + 'timestamp-warning', # timeStampWarning = True + # '-no-time-stamp-warning' # timeStampWarning = False + 'fail-on-parse' # failOnParse = True + # 'no-fail-on-parse' # failOnParse = False + +} + +if len(sys.argv) >= 4: + + for arg in sys.argv: + noArg = arg.replace('-no-', '') + + if arg in optionalArgs: + setArgumentBool(arg, True) + firstPositional += 1 + elif noArg in optionalArgs: + setArgumentBool(noArg, False) + firstPositional += 1 + +#sys.exit(0) +pathToDdl = sys.argv[firstPositional] +pathToHeader = sys.argv[firstPositional + 1] + '.h' +namespace = sys.argv[firstPositional + 2] + +INCLUDE = 'sqlpp11' +NAMESPACE = 'sqlpp' + + + # PARSER def ddlWord(string): return WordStart(alphanums + "_") + CaselessLiteral(string) + WordEnd(alphanums + "_") @@ -87,8 +126,8 @@ ddlTerm = Word(alphas, alphanums + "_$") ddlName = Or([ddlTerm, ddlString]) ddlArguments = "(" + delimitedList(Or([ddlString, ddlTerm, ddlNum])) + ")" ddlNotNull = Group(ddlWord("NOT") + ddlWord("NULL")).setResultsName("notNull") -ddlDefaultValue = ddlWord("DEFAULT").setResultsName("hasDefaultValue"); -ddlAutoValue = ddlWord("AUTO_INCREMENT").setResultsName("hasAutoValue"); +ddlDefaultValue = ddlWord("DEFAULT").setResultsName("hasDefaultValue") +ddlAutoValue = ddlWord("AUTO_INCREMENT").setResultsName("hasAutoValue") ddlColumnComment = Group(ddlWord("COMMENT") + ddlString).setResultsName("comment") ddlConstraint = Or([ ddlWord("CONSTRAINT"), @@ -98,14 +137,6 @@ ddlConstraint = Or([ ddlWord("INDEX"), ddlWord("UNIQUE"), ]) -ddlColumn = Group(Optional(ddlConstraint).setResultsName("isConstraint") + OneOrMore(MatchFirst([ddlNotNull, ddlAutoValue, ddlDefaultValue, ddlTerm, ddlNum, ddlColumnComment, ddlString, ddlArguments]))) -createTable = Group(ddlWord("CREATE") + ddlWord("TABLE") + ddlName.setResultsName("tableName") + "(" + Group(delimitedList(ddlColumn)).setResultsName("columns") + ")").setResultsName("create") - - -ddl = ZeroOrMore(Suppress(SkipTo(createTable, False)) + createTable) - -ddlComment = oneOf(["--", "#"]) + restOfLine -ddl.ignore(ddlComment) # MAP SQL TYPES types = { @@ -125,16 +156,33 @@ types = { 'boolean': 'boolean', 'double': 'floating_point', 'float': 'floating_point', - 'date' : 'day_point', - 'datetime' : 'time_point', - 'timestamp' : 'time_point', - 'enum' : 'text', # MYSQL - 'set' : 'text', # MYSQL + 'date': 'day_point', + 'datetime': 'time_point', + 'timestamp': 'time_point', + 'enum': 'text', # MYSQL + 'set': 'text', # MYSQL } +ddlColumn = Group(Optional(ddlConstraint).setResultsName("isConstraint") + OneOrMore(MatchFirst([ddlNotNull, ddlAutoValue, ddlDefaultValue, ddlTerm, ddlNum, ddlColumnComment, ddlString, ddlArguments]))) +createTable = Group(ddlWord("CREATE") + ddlWord("TABLE") + ddlName.setResultsName("tableName") + "(" + Group(delimitedList(ddlColumn)).setResultsName("columns") + ")").setResultsName("create") +ddlComment = oneOf(["--", "#"]) + restOfLine + +if failOnParse: + ddl = OneOrMore(Suppress(SkipTo(createTable, False)) + createTable) + ddl.ignore(ddlComment) + try: + tableCreations = ddl.parseFile(pathToDdl) + except ParseException as e: + print("parsing error, possible reason: can't parse default value for a field") + sys.exit() +else: + ddl = ZeroOrMore(Suppress(SkipTo(createTable, False)) + createTable) + ddl.ignore(ddlComment) + tableCreations = ddl.parseFile(pathToDdl) # PROCESS DDL -tableCreations = ddl.parseFile(pathToDdl) + + header = open(pathToHeader, 'w') print('// generated by ' + ' '.join(sys.argv), file=header) @@ -147,7 +195,7 @@ print('#include <' + INCLUDE + '/char_sequence.h>', file=header) print('', file=header) print('namespace ' + namespace, file=header) print('{', file=header) -DataTypeError = False; +DataTypeError = False for create in tableCreations: sqlTableName = create.tableName tableClass = toClassName(sqlTableName) @@ -184,22 +232,22 @@ for create in tableCreations: print(' };', file=header) print(' };', file=header) try: - traitslist = [NAMESPACE + '::' + types[sqlColumnType]]; + traitslist = [NAMESPACE + '::' + types[sqlColumnType]] except KeyError as e: print ('Error: datatype "' + sqlColumnType + '"" is not supported.') DataTypeError = True requireInsert = True if column.hasAutoValue: - traitslist.append(NAMESPACE + '::tag::must_not_insert'); - traitslist.append(NAMESPACE + '::tag::must_not_update'); + traitslist.append(NAMESPACE + '::tag::must_not_insert') + traitslist.append(NAMESPACE + '::tag::must_not_update') requireInsert = False if not column.notNull: - traitslist.append(NAMESPACE + '::tag::can_be_null'); + traitslist.append(NAMESPACE + '::tag::can_be_null') requireInsert = False if column.hasDefaultValue: requireInsert = False if requireInsert: - traitslist.append(NAMESPACE + '::tag::require_insert'); + traitslist.append(NAMESPACE + '::tag::require_insert') print(' using _traits = ' + NAMESPACE + '::make_traits<' + ', '.join(traitslist) + '>;', file=header) print(' };', file=header) print(' }', file=header)