diff options
Diffstat (limited to 'src/fmt/support')
24 files changed, 3680 insertions, 0 deletions
diff --git a/src/fmt/support/Android.mk b/src/fmt/support/Android.mk new file mode 100644 index 000000000..84a3e32f0 --- /dev/null +++ b/src/fmt/support/Android.mk @@ -0,0 +1,15 @@ +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := fmt_static +LOCAL_MODULE_FILENAME := libfmt + +LOCAL_SRC_FILES := ../src/format.cc + +LOCAL_C_INCLUDES := $(LOCAL_PATH) +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) + +LOCAL_CFLAGS += -std=c++11 -fexceptions + +include $(BUILD_STATIC_LIBRARY) + diff --git a/src/fmt/support/AndroidManifest.xml b/src/fmt/support/AndroidManifest.xml new file mode 100644 index 000000000..b5281fee1 --- /dev/null +++ b/src/fmt/support/AndroidManifest.xml @@ -0,0 +1 @@ +<manifest package="net.fmtlib" /> diff --git a/src/fmt/support/C++.sublime-syntax b/src/fmt/support/C++.sublime-syntax new file mode 100644 index 000000000..9dfb5cbe4 --- /dev/null +++ b/src/fmt/support/C++.sublime-syntax @@ -0,0 +1,2061 @@ +%YAML 1.2 +--- +# http://www.sublimetext.com/docs/3/syntax.html +name: C++ (fmt) +comment: I don't think anyone uses .hp. .cp tends to be paired with .h. (I could be wrong. :) -- chris +file_extensions: + - cpp + - cc + - cp + - cxx + - c++ + - C + - h + - hh + - hpp + - hxx + - h++ + - inl + - ipp +first_line_match: '-\*- C\+\+ -\*-' +scope: source.c++ +variables: + identifier: \b[[:alpha:]_][[:alnum:]_]*\b # upper and lowercase + macro_identifier: \b[[:upper:]_][[:upper:][:digit:]_]{2,}\b # only uppercase, at least 3 chars + path_lookahead: '(?:::\s*)?(?:{{identifier}}\s*::\s*)*(?:template\s+)?{{identifier}}' + operator_method_name: '\boperator\s*(?:[-+*/%^&|~!=<>]|[-+*/%^&|=!<>]=|<<=?|>>=?|&&|\|\||\+\+|--|,|->\*?|\(\)|\[\]|""\s*{{identifier}})' + casts: 'const_cast|dynamic_cast|reinterpret_cast|static_cast' + operator_keywords: 'and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|xor|xor_eq|noexcept' + control_keywords: 'break|case|catch|continue|default|do|else|for|goto|if|_Pragma|return|switch|throw|try|while' + memory_operators: 'new|delete' + basic_types: 'asm|__asm__|auto|bool|_Bool|char|_Complex|double|float|_Imaginary|int|long|short|signed|unsigned|void' + before_tag: 'struct|union|enum\s+class|enum\s+struct|enum|class' + declspec: '__declspec\(\s*\w+(?:\([^)]+\))?\s*\)' + storage_classes: 'static|export|extern|friend|explicit|virtual|register|thread_local' + type_qualifier: 'const|constexpr|mutable|typename|volatile' + compiler_directive: 'inline|restrict|__restrict__|__restrict' + visibility_modifiers: 'private|protected|public' + other_keywords: 'typedef|nullptr|{{visibility_modifiers}}|static_assert|sizeof|using|typeid|alignof|alignas|namespace|template' + modifiers: '{{storage_classes}}|{{type_qualifier}}|{{compiler_directive}}' + non_angle_brackets: '(?=<<|<=)' + + regular: '[^(){}&;*^%=<>-]*' + paren_open: (?:\( + paren_close: '\))?' + generic_open: (?:< + generic_close: '>)?' + balance_parentheses: '{{regular}}{{paren_open}}{{regular}}{{paren_close}}{{regular}}' + generic_lookahead: <{{regular}}{{generic_open}}{{regular}}{{generic_open}}{{regular}}{{generic_close}}\s*{{generic_close}}{{balance_parentheses}}> + + data_structures_forward_decl_lookahead: '(\s+{{macro_identifier}})*\s*(:\s*({{path_lookahead}}|{{visibility_modifiers}}|,|\s|<[^;]*>)+)?;' + non_func_keywords: 'if|for|switch|while|decltype|sizeof|__declspec|__attribute__|typeid|alignof|alignas|static_assert' + + format_spec: |- + (?x: + (?:.? [<>=^])? # fill align + [ +-]? # sign + \#? # alternate form + # technically, octal and hexadecimal integers are also supported as 'width', but rarely used + \d* # width + ,? # thousands separator + (?:\.\d+)? # precision + [bcdeEfFgGnosxX%]? # type + ) + +contexts: + main: + - include: preprocessor-global + - include: global + + ############################################################################# + # Reusable contexts + # + # The follow contexts are currently constructed to be reused in the + # Objetive-C++ syntax. They are specifically constructed to not push into + # sub-contexts, which ensures that Objective-C++ code isn't accidentally + # lexed as plain C++. + # + # The "unique-*" contexts are additions that C++ makes over C, and thus can + # be directly reused in Objective-C++ along with contexts from Objective-C + # and C. + ############################################################################# + + unique-late-expressions: + # This is highlighted after all of the other control keywords + # to allow operator overloading to be lexed properly + - match: \boperator\b + scope: keyword.control.c++ + + unique-modifiers: + - match: \b({{modifiers}})\b + scope: storage.modifier.c++ + + unique-variables: + - match: \bthis\b + scope: variable.language.c++ + # common C++ instance var naming idiom -- fMemberName + - match: '\b(f|m)[[:upper:]]\w*\b' + scope: variable.other.readwrite.member.c++ + # common C++ instance var naming idiom -- m_member_name + - match: '\bm_[[:alnum:]_]+\b' + scope: variable.other.readwrite.member.c++ + + unique-constants: + - match: \bnullptr\b + scope: constant.language.c++ + + unique-keywords: + - match: \busing\b + scope: keyword.control.c++ + - match: \bbreak\b + scope: keyword.control.flow.break.c++ + - match: \bcontinue\b + scope: keyword.control.flow.continue.c++ + - match: \bgoto\b + scope: keyword.control.flow.goto.c++ + - match: \breturn\b + scope: keyword.control.flow.return.c++ + - match: \bthrow\b + scope: keyword.control.flow.throw.c++ + - match: \b({{control_keywords}})\b + scope: keyword.control.c++ + - match: '\bdelete\b(\s*\[\])?|\bnew\b(?!])' + scope: keyword.control.c++ + - match: \b({{operator_keywords}})\b + scope: keyword.operator.word.c++ + + unique-types: + - match: \b(char16_t|char32_t|wchar_t|nullptr_t)\b + scope: storage.type.c++ + - match: \bclass\b + scope: storage.type.c++ + + unique-strings: + - match: '((?:L|u8|u|U)?R)("([^\(\)\\ ]{0,16})\()' + captures: + 1: storage.type.string.c++ + 2: punctuation.definition.string.begin.c++ + push: + - meta_scope: string.quoted.double.c++ + - match: '\)\3"' + scope: punctuation.definition.string.end.c++ + pop: true + - match: '\{\{|\}\}' + scope: constant.character.escape.c++ + - include: formatting-syntax + + unique-numbers: + - match: |- + (?x) + (?: + # floats + (?: + (?:\b\d(?:[\d']*\d)?\.\d(?:[\d']*\d)?|\B\.\d(?:[\d']*\d)?)(?:[Ee][+-]?\d(?:[\d']*\d)?)?(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))?\b + | + (?:\b\d(?:[\d']*\d)?\.)(?:\B|(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))\b|(?:[Ee][+-]?\d(?:[\d']*\d)?)(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))?\b) + | + \b\d(?:[\d']*\d)?(?:[Ee][+-]?\d(?:[\d']*\d)?)(?:[fFlL]|(?:i[fl]?|h|min|[mun]?s|_\w*))?\b + ) + | + # ints + \b(?: + (?: + # dec + [1-9](?:[\d']*\d)? + | + # oct + 0(?:[0-7']*[0-7])? + | + # hex + 0[Xx][\da-fA-F](?:[\da-fA-F']*[\da-fA-F])? + | + # bin + 0[Bb][01](?:[01']*[01])? + ) + # int suffixes + (?:(?:l{1,2}|L{1,2})[uU]?|[uU](?:l{0,2}|L{0,2})|(?:i[fl]?|h|min|[mun]?s|_\w*))?)\b + ) + (?!\.) # Number must not be followed by a decimal point + scope: constant.numeric.c++ + + identifiers: + - match: '{{identifier}}\s*(::)\s*' + captures: + 1: punctuation.accessor.c++ + - match: '(?:(::)\s*)?{{identifier}}' + captures: + 1: punctuation.accessor.c++ + + function-specifiers: + - match: \b(const|final|noexcept|override)\b + scope: storage.modifier.c++ + + ############################################################################# + # The following are C++-specific contexts that should not be reused. This is + # because they push into subcontexts and use variables that are C++-specific. + ############################################################################# + + ## Common context layout + + global: + - match: '(?=\btemplate\b)' + push: + - include: template + - match: (?=\S) + set: global-modifier + - include: namespace + - include: keywords-angle-brackets + - match: '(?={{path_lookahead}}\s*<)' + push: global-modifier + # Take care of comments just before a function definition. + - match: /\* + scope: punctuation.definition.comment.c + push: + - - match: \s*(?=\w) + set: global-modifier + - match: "" + pop: true + - - meta_scope: comment.block.c + - match: \*/ + scope: punctuation.definition.comment.c + pop: true + - include: early-expressions + - match: ^\s*\b(extern)(?=\s+"C(\+\+)?") + scope: storage.modifier.c++ + push: + - include: comments + - include: strings + - match: '\{' + scope: punctuation.section.block.begin.c++ + set: + - meta_scope: meta.extern-c.c++ + - match: '^\s*(#\s*ifdef)\s*__cplusplus\s*' + scope: meta.preprocessor.c++ + captures: + 1: keyword.control.import.c++ + set: + - match: '\}' + scope: punctuation.section.block.end.c++ + pop: true + - include: preprocessor-global + - include: global + - match: '\}' + scope: punctuation.section.block.end.c++ + pop: true + - include: preprocessor-global + - include: global + - match: (?=\S) + set: global-modifier + - match: ^\s*(?=\w) + push: global-modifier + - include: late-expressions + + statements: + - include: preprocessor-statements + - include: scope:source.c#label + - include: expressions + + expressions: + - include: early-expressions + - include: late-expressions + + early-expressions: + - include: early-expressions-before-generic-type + - include: generic-type + - include: early-expressions-after-generic-type + + early-expressions-before-generic-type: + - include: preprocessor-expressions + - include: comments + - include: case-default + - include: typedef + - include: keywords-angle-brackets + - include: keywords-parens + - include: keywords + - include: numbers + # Prevent a '<' from getting scoped as the start of another template + # parameter list, if in reality a less-than-or-equals sign is meant. + - match: <= + scope: keyword.operator.comparison.c + + early-expressions-after-generic-type: + - include: members-arrow + - include: operators + - include: members-dot + - include: strings + - include: parens + - include: brackets + - include: block + - include: variables + - include: constants + - match: ',' + scope: punctuation.separator.c++ + - match: '\)|\}' + scope: invalid.illegal.stray-bracket-end.c++ + + expressions-minus-generic-type: + - include: early-expressions-before-generic-type + - include: angle-brackets + - include: early-expressions-after-generic-type + - include: late-expressions + + expressions-minus-generic-type-function-call: + - include: early-expressions-before-generic-type + - include: angle-brackets + - include: early-expressions-after-generic-type + - include: late-expressions-before-function-call + - include: identifiers + - match: ';' + scope: punctuation.terminator.c++ + + late-expressions: + - include: late-expressions-before-function-call + - include: function-call + - include: identifiers + - match: ';' + scope: punctuation.terminator.c++ + + late-expressions-before-function-call: + - include: unique-late-expressions + - include: modifiers-parens + - include: modifiers + - include: types + + expressions-minus-function-call: + - include: early-expressions + - include: late-expressions-before-function-call + - include: identifiers + - match: ';' + scope: punctuation.terminator.c++ + + comments: + - include: scope:source.c#comments + + operators: + - include: scope:source.c#operators + + modifiers: + - include: unique-modifiers + - include: scope:source.c#modifiers + + variables: + - include: unique-variables + - include: scope:source.c#variables + + constants: + - include: unique-constants + - include: scope:source.c#constants + + keywords: + - include: unique-keywords + - include: scope:source.c#keywords + + types: + - include: unique-types + - include: types-parens + - include: scope:source.c#types + + strings: + - include: unique-strings + - match: '(L|u8|u|U)?(")' + captures: + 1: storage.type.string.c++ + 2: punctuation.definition.string.begin.c++ + push: + - meta_scope: string.quoted.double.c++ + - match: '"' + scope: punctuation.definition.string.end.c++ + pop: true + - include: scope:source.c#string_escaped_char + - match: |- + (?x)% + (\d+\$)? # field (argument #) + [#0\- +']* # flags + [,;:_]? # separator character (AltiVec) + ((-?\d+)|\*(-?\d+\$)?)? # minimum field width + (\.((-?\d+)|\*(-?\d+\$)?)?)? # precision + (hh|h|ll|l|j|t|z|q|L|vh|vl|v|hv|hl)? # length modifier + (\[[^\]]+\]|[am]s|[diouxXDOUeEfFgGaACcSspn%]) # conversion type + scope: constant.other.placeholder.c++ + - match: '\{\{|\}\}' + scope: constant.character.escape.c++ + - include: formatting-syntax + - include: scope:source.c#strings + + formatting-syntax: + # https://docs.python.org/3.6/library/string.html#formatstrings + - match: |- # simple form + (?x) + (\{) + (?: [\w.\[\]]+)? # field_name + ( ! [ars])? # conversion + ( : (?:{{format_spec}}| # format_spec OR + [^}%]*%.[^}]*) # any format-like string + )? + (\}) + scope: constant.other.placeholder.c++ + captures: + 1: punctuation.definition.placeholder.begin.c++ + 2: storage.modifier.c++onversion.c++ + 3: constant.other.format-spec.c++ + 4: punctuation.definition.placeholder.end.c++ + - match: \{(?=[^\}"']+\{[^"']*\}) # complex (nested) form + scope: punctuation.definition.placeholder.begin.c++ + push: + - meta_scope: constant.other.placeholder.c++ + - match: \} + scope: punctuation.definition.placeholder.end.c++ + pop: true + - match: '[\w.\[\]]+' + - match: '![ars]' + scope: storage.modifier.conversion.c++ + - match: ':' + push: + - meta_scope: meta.format-spec.c++ constant.other.format-spec.c++ + - match: (?=\}) + pop: true + - include: formatting-syntax + + numbers: + - include: unique-numbers + - include: scope:source.c#numbers + + ## C++-specific contexts + + case-default: + - match: '\b(default|case)\b' + scope: keyword.control.c++ + push: + - match: (?=[);,]) + pop: true + - match: ':' + scope: punctuation.separator.c++ + pop: true + - include: expressions + + modifiers-parens: + - match: '\b(alignas)\b\s*(\()' + captures: + 1: storage.modifier.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - match: \b(__attribute__)\s*(\(\() + captures: + 1: storage.modifier.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push : + - meta_scope: meta.attribute.c++ + - meta_content_scope: meta.group.c++ + - include: parens + - include: strings + - match: \)\) + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - match: \b(__declspec)(\() + captures: + 1: storage.modifier.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - match: '\b(align|allocate|code_seg|deprecated|property|uuid)\b\s*(\()' + captures: + 1: storage.modifier.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: numbers + - include: strings + - match: \b(get|put)\b + scope: variable.parameter.c++ + - match: ',' + scope: punctuation.separator.c++ + - match: '=' + scope: keyword.operator.assignment.c++ + - match: '\b(appdomain|deprecated|dllimport|dllexport|jintrinsic|naked|noalias|noinline|noreturn|nothrow|novtable|process|restrict|safebuffers|selectany|thread)\b' + scope: constant.other.c++ + + types-parens: + - match: '\b(decltype)\b\s*(\()' + captures: + 1: storage.type.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + + keywords-angle-brackets: + - match: \b({{casts}})\b\s* + scope: keyword.operator.word.cast.c++ + push: + - match: '>' + scope: punctuation.section.generic.end.c++ + pop: true + - match: '<' + scope: punctuation.section.generic.begin.c++ + push: + - match: '(?=>)' + pop: true + - include: expressions-minus-generic-type-function-call + + keywords-parens: + - match: '\b(alignof|typeid|static_assert|sizeof)\b\s*(\()' + captures: + 1: keyword.operator.word.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + + namespace: + - match: '\b(using)\s+(namespace)\s+(?={{path_lookahead}})' + captures: + 1: keyword.control.c++ + 2: keyword.control.c++ + push: + - include: identifiers + - match: '' + pop: true + - match: '\b(namespace)\s+(?=({{path_lookahead}})?(?!\s*[;,]))' + scope: meta.namespace.c++ + captures: + 1: keyword.control.c++ + push: + - meta_content_scope: meta.namespace.c++ entity.name.namespace.c++ + - include: identifiers + - match: '' + set: + - meta_scope: meta.namespace.c++ + - include: comments + - match: '=' + scope: keyword.operator.alias.c++ + - match: '(?=;)' + pop: true + - match: '\}' + scope: meta.block.c++ punctuation.section.block.end.c++ + pop: true + - match: '\{' + scope: punctuation.section.block.begin.c++ + push: + - meta_scope: meta.block.c++ + - match: '(?=\})' + pop: true + - include: preprocessor-global + - include: global + - include: expressions + + template-common: + # Exit the template scope if we hit some basic invalid characters. This + # helps when a user is in the middle of typing their template types and + # prevents re-highlighting the whole file until the next > is found. + - match: (?=[{};]) + pop: true + - include: expressions + + template: + - match: \btemplate\b + scope: storage.type.template.c++ + push: + - meta_scope: meta.template.c++ + # Explicitly include comments here at the top, in order to NOT match the + # \S lookahead in the case of comments. + - include: comments + - match: < + scope: punctuation.section.generic.begin.c++ + set: + - meta_content_scope: meta.template.c++ + - match: '>' + scope: meta.template.c++ punctuation.section.generic.end.c++ + pop: true + - match: \.{3} + scope: keyword.operator.variadic.c++ + - match: \b(typename|{{before_tag}})\b + scope: storage.type.c++ + - include: template # include template here for nested templates + - include: template-common + - match: (?=\S) + set: + - meta_content_scope: meta.template.c++ + - match: \b({{before_tag}})\b + scope: storage.type.c++ + - include: template-common + + generic-type: + - match: '(?=(?!template){{path_lookahead}}\s*{{generic_lookahead}}\s*\()' + push: + - meta_scope: meta.function-call.c++ + - match: \btemplate\b + scope: storage.type.template.c++ + - match: '(?:(::)\s*)?{{identifier}}\s*(::)\s*' + captures: + 1: punctuation.accessor.double-colon.c++ + 2: punctuation.accessor.double-colon.c++ + - match: (?:(::)\s*)?({{identifier}})\s*(<) + captures: + 1: punctuation.accessor.double-colon.c++ + 2: variable.function.c++ + 3: punctuation.section.generic.begin.c++ + push: + - match: '>' + scope: punctuation.section.generic.end.c++ + pop: true + - include: expressions-minus-generic-type-function-call + - match: (?:(::)\s*)?({{identifier}})\s*(\() + captures: + 1: punctuation.accessor.double-colon.c++ + 2: variable.function.c++ + 3: punctuation.section.group.begin.c++ + set: + - meta_scope: meta.function-call.c++ + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - include: angle-brackets + - match: '\(' + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_scope: meta.function-call.c++ + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - match: '(?=(?!template){{path_lookahead}}\s*{{generic_lookahead}})' + push: + - include: identifiers + - match: '<' + scope: punctuation.section.generic.begin.c++ + set: + - match: '>' + scope: punctuation.section.generic.end.c++ + pop: true + - include: expressions-minus-generic-type-function-call + + angle-brackets: + - match: '<(?!<)' + scope: punctuation.section.generic.begin.c++ + push: + - match: '>' + scope: punctuation.section.generic.end.c++ + pop: true + - include: expressions-minus-generic-type-function-call + + block: + - match: '\{' + scope: punctuation.section.block.begin.c++ + push: + - meta_scope: meta.block.c++ + - match: (?=^\s*#\s*(elif|else|endif)\b) + pop: true + - match: '\}' + scope: punctuation.section.block.end.c++ + pop: true + - include: statements + + function-call: + - match: (?={{path_lookahead}}\s*\() + push: + - meta_scope: meta.function-call.c++ + - include: scope:source.c#c99 + - match: '(?:(::)\s*)?{{identifier}}\s*(::)\s*' + scope: variable.function.c++ + captures: + 1: punctuation.accessor.c++ + 2: punctuation.accessor.c++ + - match: '(?:(::)\s*)?{{identifier}}' + scope: variable.function.c++ + captures: + 1: punctuation.accessor.c++ + - match: '\(' + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.function-call.c++ meta.group.c++ + - match: '\)' + scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + + members-inside-function-call: + - meta_content_scope: meta.method-call.c++ meta.group.c++ + - match: \) + scope: meta.method-call.c++ meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + + members-after-accessor-junction: + # After we've seen an accessor (dot or arrow), this context decides what + # kind of entity we're accessing. + - include: comments + - match: \btemplate\b + scope: meta.method-call.c++ storage.type.template.c++ + # Guaranteed to be a template member function call after we match this + set: + - meta_content_scope: meta.method-call.c++ + - include: comments + - match: '{{identifier}}' + scope: variable.function.member.c++ + set: + - meta_content_scope: meta.method-call.c++ + - match: \( + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: members-inside-function-call + - include: comments + - include: angle-brackets + - match: (?=\S) # safety pop + pop: true + - match: (?=\S) # safety pop + pop: true + # Operator overloading + - match: '({{operator_method_name}})\s*(\()' + captures: + 0: meta.method-call.c++ + 1: variable.function.member.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + set: members-inside-function-call + # Non-templated member function call + - match: (~?{{identifier}})\s*(\() + captures: + 0: meta.method-call.c++ + 1: variable.function.member.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + set: members-inside-function-call + # Templated member function call + - match: (~?{{identifier}})\s*(?={{generic_lookahead}}) + captures: + 1: variable.function.member.c++ + set: + - meta_scope: meta.method-call.c++ + - match: < + scope: punctuation.section.generic.begin.c++ + set: + - meta_content_scope: meta.method-call.c++ + - match: '>' + scope: punctuation.section.generic.end.c++ + set: + - meta_content_scope: meta.method-call.c++ + - include: comments + - match: \( + scope: punctuation.section.group.begin.c++ + set: members-inside-function-call + - match: (?=\S) # safety pop + pop: true + - include: expressions + # Explicit base-class access + - match: ({{identifier}})\s*(::) + captures: + 1: variable.other.base-class.c++ + 2: punctuation.accessor.double-colon.c++ + set: members-after-accessor-junction # reset + # Just a regular member variable + - match: '{{identifier}}' + scope: variable.other.readwrite.member.c++ + pop: true + + members-dot: + - include: scope:source.c#access-illegal + # No lookahead required because members-dot goes after operators in the + # early-expressions-after-generic-type context. This means triple dots + # (i.e. "..." or "variadic") is attempted first. + - match: \. + scope: punctuation.accessor.dot.c++ + push: members-after-accessor-junction + + members-arrow: + # This needs to be before operators in the + # early-expressions-after-generic-type context because otherwise the "->" + # from the C language will match. + - match: -> + scope: punctuation.accessor.arrow.c++ + push: members-after-accessor-junction + + typedef: + - match: \btypedef\b + scope: storage.type.c++ + push: + - match: ({{identifier}})?\s*(?=;) + captures: + 1: entity.name.type.typedef.c++ + pop: true + - match: \b(struct)\s+({{identifier}})\b + captures: + 1: storage.type.c++ + - include: expressions-minus-generic-type + + parens: + - match: \( + scope: punctuation.section.group.begin.c++ + push: + - meta_scope: meta.group.c++ + - match: \) + scope: punctuation.section.group.end.c++ + pop: true + - include: expressions + + brackets: + - match: \[ + scope: punctuation.section.brackets.begin.c++ + push: + - meta_scope: meta.brackets.c++ + - match: \] + scope: punctuation.section.brackets.end.c++ + pop: true + - include: expressions + + function-trailing-return-type: + - match: '{{non_angle_brackets}}' + pop: true + - include: angle-brackets + - include: types + - include: modifiers-parens + - include: modifiers + - include: identifiers + - match: \*|& + scope: keyword.operator.c++ + - include: function-trailing-return-type-parens + - match: '(?=\S)' + pop: true + + function-trailing-return-type-parens: + - match: \( + scope: punctuation.section.group.begin.c++ + push: + - meta_scope: meta.group.c++ + - match: \) + scope: punctuation.section.group.end.c++ + pop: true + - include: function-trailing-return-type + + ## Detection of function and data structure definitions at the global level + + global-modifier: + - include: comments + - include: modifiers-parens + - include: modifiers + # Constructors and destructors don't have a type + - match: '(?={{path_lookahead}}\s*::\s*{{identifier}}\s*(\(|$))' + set: + - meta_content_scope: meta.function.c++ entity.name.function.constructor.c++ + - include: identifiers + - match: '(?=[^\w\s])' + set: function-definition-params + - match: '(?={{path_lookahead}}\s*::\s*~{{identifier}}\s*(\(|$))' + set: + - meta_content_scope: meta.function.c++ entity.name.function.destructor.c++ + - include: identifiers + - match: '~{{identifier}}' + - match: '(?=[^\w\s])' + set: function-definition-params + # If we see a path ending in :: before a newline, we don't know if it is + # a constructor or destructor, or a long return type, so we are just going + # to treat it like a regular function. Most likely it is a constructor, + # since it doesn't seem most developers would create such a long typename. + - match: '(?={{path_lookahead}}\s*::\s*$)' + set: + - meta_content_scope: meta.function.c++ entity.name.function.c++ + - include: identifiers + - match: '~{{identifier}}' + - match: '(?=[^\w\s])' + set: function-definition-params + - include: unique-strings + - match: '(?=\S)' + set: global-type + + global-type: + - include: comments + - match: \*|& + scope: keyword.operator.c++ + - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}}|operator)\b)' + pop: true + - match: '(?=\s)' + set: global-maybe-function + # If a class/struct/enum followed by a name that is not a macro or declspec + # then this is likely a return type of a function. This is uncommon. + - match: |- + (?x: + ({{before_tag}}) + \s+ + (?= + (?![[:upper:][:digit:]_]+\b|__declspec|{{before_tag}}) + {{path_lookahead}} + (\s+{{identifier}}\s*\(|\s*[*&]) + ) + ) + captures: + 1: storage.type.c++ + set: + - include: identifiers + - match: '' + set: global-maybe-function + # The previous match handles return types of struct/enum/etc from a func, + # there this one exits the context to allow matching an actual struct/class + - match: '(?=\b({{before_tag}})\b)' + set: data-structures + - match: '(?=\b({{casts}})\b\s*<)' + pop: true + - match: '{{non_angle_brackets}}' + pop: true + - include: angle-brackets + - include: types + # Allow a macro call + - match: '({{identifier}})\s*(\()(?=[^\)]+\))' + captures: + 1: variable.function.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_scope: meta.function-call.c++ + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - match: '(?={{path_lookahead}}\s*\()' + set: + - include: function-call + - match: '' + pop: true + - include: variables + - include: constants + - include: identifiers + - match: (?=\W) + pop: true + + global-maybe-function: + - include: comments + # Consume pointer info, macros and any type info that was offset by macros + - match: \*|& + scope: keyword.operator.c++ + - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}})\b)' + pop: true + - match: '\b({{type_qualifier}})\b' + scope: storage.modifier.c++ + - match: '{{non_angle_brackets}}' + pop: true + - include: angle-brackets + - include: types + - include: modifiers-parens + - include: modifiers + # All uppercase identifier just before a newline is most likely a macro + - match: '[[:upper:][:digit:]_]+\s*$' + # Operator overloading + - match: '(?=({{path_lookahead}}\s*(?:{{generic_lookahead}})?::\s*)?{{operator_method_name}}\s*(\(|$))' + set: + - meta_content_scope: meta.function.c++ entity.name.function.c++ + - include: identifiers + - match: '(?=\s*(\(|$))' + set: function-definition-params + # Identifier that is not the function name - likely a macro or type + - match: '(?={{path_lookahead}}([ \t]+|[*&])(?!\s*(<|::|\(|$)))' + push: + - include: identifiers + - match: '' + pop: true + # Real function definition + - match: '(?={{path_lookahead}}({{generic_lookahead}}({{path_lookahead}})?)\s*(\(|$))' + set: [function-definition-params, global-function-identifier-generic] + - match: '(?={{path_lookahead}}\s*(\(|$))' + set: [function-definition-params, global-function-identifier] + - match: '(?={{path_lookahead}}\s*::\s*$)' + set: [function-definition-params, global-function-identifier] + - match: '(?=\S)' + pop: true + + global-function-identifier-generic: + - include: angle-brackets + - match: '::' + scope: punctuation.accessor.c++ + - match: '(?={{identifier}}<.*>\s*\()' + push: + - meta_content_scope: entity.name.function.c++ + - include: identifiers + - match: '(?=<)' + pop: true + - match: '(?={{identifier}}\s*\()' + push: + - meta_content_scope: entity.name.function.c++ + - include: identifiers + - match: '' + pop: true + - match: '(?=\()' + pop: true + + global-function-identifier: + - meta_content_scope: entity.name.function.c++ + - include: identifiers + - match: '(?=\S)' + pop: true + + function-definition-params: + - meta_content_scope: meta.function.c++ + - include: comments + - match: '(?=\()' + set: + - match: \( + scope: meta.function.parameters.c++ meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.function.parameters.c++ meta.group.c++ + - match : \) + scope: punctuation.section.group.end.c++ + set: function-definition-continue + - match: '\bvoid\b' + scope: storage.type.c++ + - match: '{{identifier}}(?=\s*(\[|,|\)|=))' + scope: variable.parameter.c++ + - match: '=' + scope: keyword.operator.assignment.c++ + push: + - match: '(?=,|\))' + pop: true + - include: expressions-minus-generic-type + - include: scope:source.c#preprocessor-line-continuation + - include: expressions-minus-generic-type + - include: scope:source.c#preprocessor-line-continuation + - match: (?=\S) + pop: true + + function-definition-continue: + - meta_content_scope: meta.function.c++ + - include: comments + - match: '(?=;)' + pop: true + - match: '->' + scope: punctuation.separator.c++ + set: function-definition-trailing-return + - include: function-specifiers + - match: '=' + scope: keyword.operator.assignment.c++ + - match: '&' + scope: keyword.operator.c++ + - match: \b0\b + scope: constant.numeric.c++ + - match: \b(default|delete)\b + scope: storage.modifier.c++ + - match: '(?=\{)' + set: function-definition-body + - match: '(?=\S)' + pop: true + + function-definition-trailing-return: + - include: comments + - match: '(?=;)' + pop: true + - match: '(?=\{)' + set: function-definition-body + - include: function-specifiers + - include: function-trailing-return-type + + function-definition-body: + - meta_content_scope: meta.function.c++ meta.block.c++ + - match: '\{' + scope: punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.function.c++ meta.block.c++ + - match: '\}' + scope: meta.function.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - match: (?=^\s*#\s*(elif|else|endif)\b) + pop: true + - match: '(?=({{before_tag}})([^(;]+$|.*\{))' + push: data-structures + - include: statements + + ## Data structures including classes, structs, unions and enums + + data-structures: + - match: '\bclass\b' + scope: storage.type.c++ + set: data-structures-class-definition + # Detect variable type definitions using struct/enum/union followed by a tag + - match: '\b({{before_tag}})(?=\s+{{path_lookahead}}\s+{{path_lookahead}}\s*[=;\[])' + scope: storage.type.c++ + - match: '\bstruct\b' + scope: storage.type.c++ + set: data-structures-struct-definition + - match: '\benum(\s+(class|struct))?\b' + scope: storage.type.c++ + set: data-structures-enum-definition + - match: '\bunion\b' + scope: storage.type.c++ + set: data-structures-union-definition + - match: '(?=\S)' + pop: true + + preprocessor-workaround-eat-macro-before-identifier: + # Handle macros so they aren't matched as the class name + - match: ({{macro_identifier}})(?=\s+~?{{identifier}}) + captures: + 1: meta.assumed-macro.c + + data-structures-class-definition: + - meta_scope: meta.class.c++ + - include: data-structures-definition-common-begin + - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' + scope: entity.name.class.forward-decl.c++ + set: data-structures-class-definition-after-identifier + - match: '{{identifier}}' + scope: entity.name.class.c++ + set: data-structures-class-definition-after-identifier + - match: '(?=[:{])' + set: data-structures-class-definition-after-identifier + - match: '(?=;)' + pop: true + + data-structures-class-definition-after-identifier: + - meta_content_scope: meta.class.c++ + - include: data-structures-definition-common-begin + # No matching of identifiers since they should all be macros at this point + - include: data-structures-definition-common-end + - match: '\{' + scope: meta.block.c++ punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.class.c++ meta.block.c++ + - match: '\}' + scope: meta.class.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - include: data-structures-body + + data-structures-struct-definition: + - meta_scope: meta.struct.c++ + - include: data-structures-definition-common-begin + - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' + scope: entity.name.struct.forward-decl.c++ + set: data-structures-struct-definition-after-identifier + - match: '{{identifier}}' + scope: entity.name.struct.c++ + set: data-structures-struct-definition-after-identifier + - match: '(?=[:{])' + set: data-structures-struct-definition-after-identifier + - match: '(?=;)' + pop: true + + data-structures-struct-definition-after-identifier: + - meta_content_scope: meta.struct.c++ + - include: data-structures-definition-common-begin + # No matching of identifiers since they should all be macros at this point + - include: data-structures-definition-common-end + - match: '\{' + scope: meta.block.c++ punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.struct.c++ meta.block.c++ + - match: '\}' + scope: meta.struct.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - include: data-structures-body + + data-structures-enum-definition: + - meta_scope: meta.enum.c++ + - include: data-structures-definition-common-begin + - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' + scope: entity.name.enum.forward-decl.c++ + set: data-structures-enum-definition-after-identifier + - match: '{{identifier}}' + scope: entity.name.enum.c++ + set: data-structures-enum-definition-after-identifier + - match: '(?=[:{])' + set: data-structures-enum-definition-after-identifier + - match: '(?=;)' + pop: true + + data-structures-enum-definition-after-identifier: + - meta_content_scope: meta.enum.c++ + - include: data-structures-definition-common-begin + # No matching of identifiers since they should all be macros at this point + - include: data-structures-definition-common-end + - match: '\{' + scope: meta.block.c++ punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.enum.c++ meta.block.c++ + # Enums don't support methods so we have a simplified body + - match: '\}' + scope: meta.enum.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - include: statements + + data-structures-union-definition: + - meta_scope: meta.union.c++ + - include: data-structures-definition-common-begin + - match: '{{identifier}}(?={{data_structures_forward_decl_lookahead}})' + scope: entity.name.union.forward-decl.c++ + set: data-structures-union-definition-after-identifier + - match: '{{identifier}}' + scope: entity.name.union.c++ + set: data-structures-union-definition-after-identifier + - match: '(?=[{])' + set: data-structures-union-definition-after-identifier + - match: '(?=;)' + pop: true + + data-structures-union-definition-after-identifier: + - meta_content_scope: meta.union.c++ + - include: data-structures-definition-common-begin + # No matching of identifiers since they should all be macros at this point + # Unions don't support base classes + - include: angle-brackets + - match: '\{' + scope: meta.block.c++ punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.union.c++ meta.block.c++ + - match: '\}' + scope: meta.union.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - include: data-structures-body + - match: '(?=;)' + pop: true + + data-structures-definition-common-begin: + - include: comments + - match: '(?=\b(?:{{before_tag}}|{{control_keywords}})\b)' + pop: true + - include: preprocessor-other + - include: modifiers-parens + - include: modifiers + - include: preprocessor-workaround-eat-macro-before-identifier + + data-structures-definition-common-end: + - include: angle-brackets + - match: \bfinal\b + scope: storage.modifier.c++ + - match: ':' + scope: punctuation.separator.c++ + push: + - include: comments + - include: preprocessor-other + - include: modifiers-parens + - include: modifiers + - match: '\b(virtual|{{visibility_modifiers}})\b' + scope: storage.modifier.c++ + - match: (?={{path_lookahead}}) + push: + - meta_scope: entity.other.inherited-class.c++ + - include: identifiers + - match: '' + pop: true + - include: angle-brackets + - match: ',' + scope: punctuation.separator.c++ + - match: (?=\{|;) + pop: true + - match: '(?=;)' + pop: true + + data-structures-body: + - include: preprocessor-data-structures + - match: '(?=\btemplate\b)' + push: + - include: template + - match: (?=\S) + set: data-structures-modifier + - include: typedef + - match: \b({{visibility_modifiers}})\s*(:)(?!:) + captures: + 1: storage.modifier.c++ + 2: punctuation.section.class.c++ + - match: '^\s*(?=(?:~?\w+|::))' + push: data-structures-modifier + - include: expressions-minus-generic-type + + data-structures-modifier: + - match: '\bfriend\b' + scope: storage.modifier.c++ + push: + - match: (?=;) + pop: true + - match: '\{' + scope: punctuation.section.block.begin.c++ + set: + - meta_scope: meta.block.c++ + - match: '\}' + scope: punctuation.section.block.end.c++ + pop: true + - include: statements + - match: '\b({{before_tag}})\b' + scope: storage.type.c++ + - include: expressions-minus-function-call + - include: comments + - include: modifiers-parens + - include: modifiers + - match: '\bstatic_assert(?=\s*\()' + scope: meta.static-assert.c++ keyword.operator.word.c++ + push: + - match: '\(' + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.function-call.c++ meta.group.c++ + - match: '\)' + scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + # Destructor + - match: '(?:{{identifier}}\s*(::)\s*)?~{{identifier}}(?=\s*(\(|$))' + scope: meta.method.destructor.c++ entity.name.function.destructor.c++ + captures: + 1: punctuation.accessor.c++ + set: method-definition-params + # It's a macro, not a constructor if there is no type in the first param + - match: '({{identifier}})\s*(\()(?=\s*(?!void){{identifier}}\s*[),])' + captures: + 1: variable.function.c++ + 2: meta.group.c++ punctuation.section.group.begin.c++ + push: + - meta_scope: meta.function-call.c++ + - meta_content_scope: meta.group.c++ + - match: '\)' + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + # Constructor + - include: preprocessor-workaround-eat-macro-before-identifier + - match: '((?!{{before_tag}}|template){{identifier}})(?=\s*\()' + scope: meta.method.constructor.c++ entity.name.function.constructor.c++ + set: method-definition-params + # Long form constructor + - match: '({{identifier}}\s*(::)\s*{{identifier}})(?=\s*\()' + captures: + 1: meta.method.constructor.c++ entity.name.function.constructor.c++ + 2: punctuation.accessor.c++ + push: method-definition-params + - match: '(?=\S)' + set: data-structures-type + + data-structures-type: + - include: comments + - match: \*|& + scope: keyword.operator.c++ + # Cast methods + - match: '(operator)\s+({{identifier}})(?=\s*(\(|$))' + captures: + 1: keyword.control.c++ + 2: meta.method.c++ entity.name.function.c++ + set: method-definition-params + - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}}|operator)\b)' + pop: true + - match: '(?=\s)' + set: data-structures-maybe-method + # If a class/struct/enum followed by a name that is not a macro or declspec + # then this is likely a return type of a function. This is uncommon. + - match: |- + (?x: + ({{before_tag}}) + \s+ + (?= + (?![[:upper:][:digit:]_]+\b|__declspec|{{before_tag}}) + {{path_lookahead}} + (\s+{{identifier}}\s*\(|\s*[*&]) + ) + ) + captures: + 1: storage.type.c++ + set: + - include: identifiers + - match: '' + set: data-structures-maybe-method + # The previous match handles return types of struct/enum/etc from a func, + # there this one exits the context to allow matching an actual struct/class + - match: '(?=\b({{before_tag}})\b)' + set: data-structures + - match: '(?=\b({{casts}})\b\s*<)' + pop: true + - match: '{{non_angle_brackets}}' + pop: true + - include: angle-brackets + - include: types + - include: variables + - include: constants + - include: identifiers + - match: (?=[&*]) + set: data-structures-maybe-method + - match: (?=\W) + pop: true + + data-structures-maybe-method: + - include: comments + # Consume pointer info, macros and any type info that was offset by macros + - match: \*|& + scope: keyword.operator.c++ + - match: '(?=\b({{control_keywords}}|{{operator_keywords}}|{{casts}}|{{memory_operators}}|{{other_keywords}})\b)' + pop: true + - match: '\b({{type_qualifier}})\b' + scope: storage.modifier.c++ + - match: '{{non_angle_brackets}}' + pop: true + - include: angle-brackets + - include: types + - include: modifiers-parens + - include: modifiers + # Operator overloading + - match: '{{operator_method_name}}(?=\s*(\(|$))' + scope: meta.method.c++ entity.name.function.c++ + set: method-definition-params + # Identifier that is not the function name - likely a macro or type + - match: '(?={{path_lookahead}}([ \t]+|[*&])(?!\s*(<|::|\()))' + push: + - include: identifiers + - match: '' + pop: true + # Real function definition + - match: '(?={{path_lookahead}}({{generic_lookahead}})\s*(\())' + set: [method-definition-params, data-structures-function-identifier-generic] + - match: '(?={{path_lookahead}}\s*(\())' + set: [method-definition-params, data-structures-function-identifier] + - match: '(?={{path_lookahead}}\s*::\s*$)' + set: [method-definition-params, data-structures-function-identifier] + - match: '(?=\S)' + pop: true + + data-structures-function-identifier-generic: + - include: angle-brackets + - match: '(?={{identifier}})' + push: + - meta_content_scope: entity.name.function.c++ + - include: identifiers + - match: '(?=<)' + pop: true + - match: '(?=\()' + pop: true + + data-structures-function-identifier: + - meta_content_scope: entity.name.function.c++ + - include: identifiers + - match: '(?=\S)' + pop: true + + method-definition-params: + - meta_content_scope: meta.method.c++ + - include: comments + - match: '(?=\()' + set: + - match: \( + scope: meta.method.parameters.c++ meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.method.parameters.c++ meta.group.c++ + - match : \) + scope: punctuation.section.group.end.c++ + set: method-definition-continue + - match: '\bvoid\b' + scope: storage.type.c++ + - match: '{{identifier}}(?=\s*(\[|,|\)|=))' + scope: variable.parameter.c++ + - match: '=' + scope: keyword.operator.assignment.c++ + push: + - match: '(?=,|\))' + pop: true + - include: expressions-minus-generic-type + - include: expressions-minus-generic-type + - match: '(?=\S)' + pop: true + + method-definition-continue: + - meta_content_scope: meta.method.c++ + - include: comments + - match: '(?=;)' + pop: true + - match: '->' + scope: punctuation.separator.c++ + set: method-definition-trailing-return + - include: function-specifiers + - match: '=' + scope: keyword.operator.assignment.c++ + - match: '&' + scope: keyword.operator.c++ + - match: \b0\b + scope: constant.numeric.c++ + - match: \b(default|delete)\b + scope: storage.modifier.c++ + - match: '(?=:)' + set: + - match: ':' + scope: punctuation.separator.initializer-list.c++ + set: + - meta_scope: meta.method.constructor.initializer-list.c++ + - match: '{{identifier}}' + scope: variable.other.readwrite.member.c++ + push: + - match: \( + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.group.c++ + - match: \) + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - match: \{ + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: + - meta_content_scope: meta.group.c++ + - match: \} + scope: meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + - include: comments + - match: (?=\{|;) + set: method-definition-continue + - include: expressions + - match: '(?=\{)' + set: method-definition-body + - match: '(?=\S)' + pop: true + + method-definition-trailing-return: + - include: comments + - match: '(?=;)' + pop: true + - match: '(?=\{)' + set: method-definition-body + - include: function-specifiers + - include: function-trailing-return-type + + method-definition-body: + - meta_content_scope: meta.method.c++ meta.block.c++ + - match: '\{' + scope: punctuation.section.block.begin.c++ + set: + - meta_content_scope: meta.method.c++ meta.block.c++ + - match: '\}' + scope: meta.method.c++ meta.block.c++ punctuation.section.block.end.c++ + pop: true + - match: (?=^\s*#\s*(elif|else|endif)\b) + pop: true + - match: '(?=({{before_tag}})([^(;]+$|.*\{))' + push: data-structures + - include: statements + + ## Preprocessor for data-structures + + preprocessor-data-structures: + - include: preprocessor-rule-enabled-data-structures + - include: preprocessor-rule-disabled-data-structures + - include: preprocessor-practical-workarounds + + preprocessor-rule-disabled-data-structures: + - match: ^\s*((#if)\s+(0))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - match: (?=^\s*#\s*endif\b) + pop: true + - include: negated-block + - include: data-structures-body + - match: "" + push: + - meta_scope: comment.block.preprocessor.if-branch.c++ + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: scope:source.c#preprocessor-disabled + + preprocessor-rule-enabled-data-structures: + - match: ^\s*((#if)\s+(0*1))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - meta_content_scope: comment.block.preprocessor.else-branch.c++ + - match: (?=^\s*#\s*endif\b) + pop: true + - include: scope:source.c#preprocessor-disabled + - match: "" + push: + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: negated-block + - include: data-structures-body + + ## Preprocessor for global + + preprocessor-global: + - include: preprocessor-rule-enabled-global + - include: preprocessor-rule-disabled-global + - include: preprocessor-rule-other-global + + preprocessor-statements: + - include: preprocessor-rule-enabled-statements + - include: preprocessor-rule-disabled-statements + - include: preprocessor-rule-other-statements + + preprocessor-expressions: + - include: scope:source.c#incomplete-inc + - include: preprocessor-macro-define + - include: scope:source.c#pragma-mark + - include: preprocessor-other + + preprocessor-rule-disabled-global: + - match: ^\s*((#if)\s+(0))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - match: (?=^\s*#\s*endif\b) + pop: true + - include: preprocessor-global + - include: negated-block + - include: global + - match: "" + push: + - meta_scope: comment.block.preprocessor.if-branch.c++ + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: scope:source.c#preprocessor-disabled + + preprocessor-rule-enabled-global: + - match: ^\s*((#if)\s+(0*1))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - meta_content_scope: comment.block.preprocessor.else-branch.c++ + - match: (?=^\s*#\s*endif\b) + pop: true + - include: scope:source.c#preprocessor-disabled + - match: "" + push: + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: preprocessor-global + - include: negated-block + - include: global + + preprocessor-rule-other-global: + - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b + captures: + 1: keyword.control.import.c++ + push: + - meta_scope: meta.preprocessor.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-comments + - match: \bdefined\b + scope: keyword.control.c++ + # Enter a new scope where all elif/else branches have their + # contexts popped by a subsequent elif/else/endif. This ensures that + # preprocessor branches don't push multiple meta.block scopes on + # the stack, thus messing up the "global" context's detection of + # functions. + - match: $\n + set: preprocessor-if-branch-global + + # These gymnastics here ensure that we are properly handling scope even + # when the preprocessor is used to create different scope beginnings, such + # as a different if/while condition + preprocessor-if-branch-global: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: (?=^\s*#\s*(elif|else)\b) + push: preprocessor-elif-else-branch-global + - match: \{ + scope: punctuation.section.block.begin.c++ + set: preprocessor-block-if-branch-global + - include: preprocessor-global + - include: negated-block + - include: global + + preprocessor-block-if-branch-global: + - meta_scope: meta.block.c++ + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-block-finish-global + - match: (?=^\s*#\s*(elif|else)\b) + push: preprocessor-elif-else-branch-global + - match: \} + scope: punctuation.section.block.end.c++ + set: preprocessor-if-branch-global + - include: statements + + preprocessor-block-finish-global: + - meta_scope: meta.block.c++ + - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-block-finish-if-branch-global + - match: \} + scope: punctuation.section.block.end.c++ + pop: true + - include: statements + + preprocessor-block-finish-if-branch-global: + - match: ^\s*(#\s*endif)\b + captures: + 1: keyword.control.import.c++ + pop: true + - match: \} + scope: punctuation.section.block.end.c++ + set: preprocessor-if-branch-global + - include: statements + + preprocessor-elif-else-branch-global: + - match: (?=^\s*#\s*(endif)\b) + pop: true + - include: preprocessor-global + - include: negated-block + - include: global + + ## Preprocessor for statements + + preprocessor-rule-disabled-statements: + - match: ^\s*((#if)\s+(0))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - match: (?=^\s*#\s*endif\b) + pop: true + - include: negated-block + - include: statements + - match: "" + push: + - meta_scope: comment.block.preprocessor.if-branch.c++ + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: scope:source.c#preprocessor-disabled + + preprocessor-rule-enabled-statements: + - match: ^\s*((#if)\s+(0*1))\b + captures: + 1: meta.preprocessor.c++ + 2: keyword.control.import.c++ + 3: constant.numeric.preprocessor.c++ + push: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: ^\s*(#\s*else)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.else.c++ + push: + - meta_content_scope: comment.block.preprocessor.else-branch.c++ + - match: (?=^\s*#\s*endif\b) + pop: true + - include: scope:source.c#preprocessor-disabled + - match: "" + push: + - match: (?=^\s*#\s*(else|endif)\b) + pop: true + - include: negated-block + - include: statements + + preprocessor-rule-other-statements: + - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b + captures: + 1: keyword.control.import.c++ + push: + - meta_scope: meta.preprocessor.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-comments + - match: \bdefined\b + scope: keyword.control.c++ + # Enter a new scope where all elif/else branches have their + # contexts popped by a subsequent elif/else/endif. This ensures that + # preprocessor branches don't push multiple meta.block scopes on + # the stack, thus messing up the "global" context's detection of + # functions. + - match: $\n + set: preprocessor-if-branch-statements + + # These gymnastics here ensure that we are properly handling scope even + # when the preprocessor is used to create different scope beginnings, such + # as a different if/while condition + preprocessor-if-branch-statements: + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + pop: true + - match: (?=^\s*#\s*(elif|else)\b) + push: preprocessor-elif-else-branch-statements + - match: \{ + scope: punctuation.section.block.begin.c++ + set: preprocessor-block-if-branch-statements + - match: (?=(?!{{non_func_keywords}}){{path_lookahead}}\s*\() + set: preprocessor-if-branch-function-call + - include: negated-block + - include: statements + + preprocessor-if-branch-function-call: + - meta_content_scope: meta.function-call.c++ + - include: scope:source.c#c99 + - match: '(?:(::)\s*)?{{identifier}}\s*(::)\s*' + scope: variable.function.c++ + captures: + 1: punctuation.accessor.c++ + 2: punctuation.accessor.c++ + - match: '(?:(::)\s*)?{{identifier}}' + scope: variable.function.c++ + captures: + 1: punctuation.accessor.c++ + - match: '\(' + scope: meta.group.c++ punctuation.section.group.begin.c++ + set: preprocessor-if-branch-function-call-arguments + + preprocessor-if-branch-function-call-arguments: + - meta_content_scope: meta.function-call.c++ meta.group.c++ + - match : \) + scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ + set: preprocessor-if-branch-statements + - match: ^\s*(#\s*(?:elif|else))\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-if-branch-statements + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-if-branch-function-call-arguments-finish + - include: expressions + + preprocessor-if-branch-function-call-arguments-finish: + - meta_content_scope: meta.function-call.c++ meta.group.c++ + - match: \) + scope: meta.function-call.c++ meta.group.c++ punctuation.section.group.end.c++ + pop: true + - include: expressions + + preprocessor-block-if-branch-statements: + - meta_scope: meta.block.c++ + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-block-finish-statements + - match: (?=^\s*#\s*(elif|else)\b) + push: preprocessor-elif-else-branch-statements + - match: \} + scope: punctuation.section.block.end.c++ + set: preprocessor-if-branch-statements + - include: statements + + preprocessor-block-finish-statements: + - meta_scope: meta.block.c++ + - match: ^\s*(#\s*(?:if|ifdef|ifndef))\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + set: preprocessor-block-finish-if-branch-statements + - match: \} + scope: punctuation.section.block.end.c++ + pop: true + - include: statements + + preprocessor-block-finish-if-branch-statements: + - match: ^\s*(#\s*endif)\b + captures: + 1: keyword.control.import.c++ + pop: true + - match: \} + scope: meta.block.c++ punctuation.section.block.end.c++ + set: preprocessor-if-branch-statements + - include: statements + + preprocessor-elif-else-branch-statements: + - match: (?=^\s*#\s*endif\b) + pop: true + - include: negated-block + - include: statements + + ## Preprocessor other + + negated-block: + - match: '\}' + scope: punctuation.section.block.end.c++ + push: + - match: '\{' + scope: punctuation.section.block.begin.c++ + pop: true + - match: (?=^\s*#\s*(elif|else|endif)\b) + pop: true + - include: statements + + preprocessor-macro-define: + - match: ^\s*(\#\s*define)\b + captures: + 1: meta.preprocessor.macro.c++ keyword.control.import.define.c++ + push: + - meta_content_scope: meta.preprocessor.macro.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-line-ending + - include: scope:source.c#preprocessor-comments + - match: '({{identifier}})(?=\()' + scope: entity.name.function.preprocessor.c++ + set: + - match: '\(' + scope: punctuation.section.group.begin.c++ + set: preprocessor-macro-params + - match: '{{identifier}}' + scope: entity.name.constant.preprocessor.c++ + set: preprocessor-macro-definition + + preprocessor-macro-params: + - meta_scope: meta.preprocessor.macro.parameters.c++ meta.group.c++ + - match: '{{identifier}}' + scope: variable.parameter.c++ + - match: \) + scope: punctuation.section.group.end.c++ + set: preprocessor-macro-definition + - match: ',' + scope: punctuation.separator.c++ + push: + - match: '{{identifier}}' + scope: variable.parameter.c++ + pop: true + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-comments + - match: '\.\.\.' + scope: keyword.operator.variadic.c++ + - match: '(?=\))' + pop: true + - match: (/\*).*(\*/) + scope: comment.block.c++ + captures: + 1: punctuation.definition.comment.c++ + 2: punctuation.definition.comment.c++ + - match: '\S+' + scope: invalid.illegal.unexpected-character.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-comments + - match: '\.\.\.' + scope: keyword.operator.variadic.c++ + - match: (/\*).*(\*/) + scope: comment.block.c++ + captures: + 1: punctuation.definition.comment.c++ + 2: punctuation.definition.comment.c++ + - match: $\n + scope: invalid.illegal.unexpected-end-of-line.c++ + + preprocessor-macro-definition: + - meta_content_scope: meta.preprocessor.macro.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-line-ending + - include: scope:source.c#preprocessor-comments + # Don't define blocks in define statements + - match: '\{' + scope: punctuation.section.block.begin.c++ + - match: '\}' + scope: punctuation.section.block.end.c++ + - include: expressions + + preprocessor-practical-workarounds: + - include: preprocessor-convention-ignore-uppercase-ident-lines + - include: scope:source.c#preprocessor-convention-ignore-uppercase-calls-without-semicolon + + preprocessor-convention-ignore-uppercase-ident-lines: + - match: ^(\s*{{macro_identifier}})+\s*$ + scope: meta.assumed-macro.c++ + push: + # It's possible that we are dealing with a function return type on its own line, and the + # name of the function is on the subsequent line. + - match: '(?={{path_lookahead}}({{generic_lookahead}}({{path_lookahead}})?)\s*\()' + set: [function-definition-params, global-function-identifier-generic] + - match: '(?={{path_lookahead}}\s*\()' + set: [function-definition-params, global-function-identifier] + - match: ^ + pop: true + + preprocessor-other: + - match: ^\s*(#\s*(?:if|ifdef|ifndef|elif|else|line|pragma|undef))\b + captures: + 1: keyword.control.import.c++ + push: + - meta_scope: meta.preprocessor.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-line-ending + - include: scope:source.c#preprocessor-comments + - match: \bdefined\b + scope: keyword.control.c++ + - match: ^\s*(#\s*endif)\b + captures: + 1: meta.preprocessor.c++ keyword.control.import.c++ + - match: ^\s*(#\s*(?:error|warning))\b + captures: + 1: keyword.control.import.error.c++ + push: + - meta_scope: meta.preprocessor.diagnostic.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-line-ending + - include: scope:source.c#preprocessor-comments + - include: strings + - match: '\S+' + scope: string.unquoted.c++ + - match: ^\s*(#\s*(?:include|include_next|import))\b + captures: + 1: keyword.control.import.include.c++ + push: + - meta_scope: meta.preprocessor.include.c++ + - include: scope:source.c#preprocessor-line-continuation + - include: scope:source.c#preprocessor-line-ending + - include: scope:source.c#preprocessor-comments + - match: '"' + scope: punctuation.definition.string.begin.c++ + push: + - meta_scope: string.quoted.double.include.c++ + - match: '"' + scope: punctuation.definition.string.end.c++ + pop: true + - match: < + scope: punctuation.definition.string.begin.c++ + push: + - meta_scope: string.quoted.other.lt-gt.include.c++ + - match: '>' + scope: punctuation.definition.string.end.c++ + pop: true + - include: preprocessor-practical-workarounds diff --git a/src/fmt/support/README b/src/fmt/support/README new file mode 100644 index 000000000..e7fbacc73 --- /dev/null +++ b/src/fmt/support/README @@ -0,0 +1,6 @@ +This directory contains build support files such as + +* CMake modules +* Build scripts +* qmake (static build with dynamic libc only) + diff --git a/src/fmt/support/Vagrantfile b/src/fmt/support/Vagrantfile new file mode 100644 index 000000000..24f166a10 --- /dev/null +++ b/src/fmt/support/Vagrantfile @@ -0,0 +1,19 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# A vagrant config for testing against gcc-4.8. +Vagrant.configure("2") do |config| + config.vm.box = "ubuntu/xenial64" + + config.vm.provider "virtualbox" do |vb| + vb.memory = "4096" + end + + config.vm.provision "shell", inline: <<-SHELL + apt-get update + apt-get install -y g++ make wget git + wget -q https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.tar.gz + tar xzf cmake-3.14.4-Linux-x86_64.tar.gz + ln -s `pwd`/cmake-3.14.4-Linux-x86_64/bin/cmake /usr/local/bin + SHELL +end diff --git a/src/fmt/support/appveyor-build.py b/src/fmt/support/appveyor-build.py new file mode 100644 index 000000000..654461032 --- /dev/null +++ b/src/fmt/support/appveyor-build.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# Build the project on AppVeyor. + +import os +from subprocess import check_call + +build = os.environ['BUILD'] +config = os.environ['CONFIGURATION'] +platform = os.environ['PLATFORM'] +path = os.environ['PATH'] +image = os.environ['APPVEYOR_BUILD_WORKER_IMAGE'] +jobid = os.environ['APPVEYOR_JOB_ID'] +cmake_command = ['cmake', '-DFMT_PEDANTIC=ON', '-DCMAKE_BUILD_TYPE=' + config, '..'] +if build == 'mingw': + cmake_command.append('-GMinGW Makefiles') + build_command = ['mingw32-make', '-j4'] + test_command = ['mingw32-make', 'test'] + # Remove the path to Git bin directory from $PATH because it breaks + # MinGW config. + path = path.replace(r'C:\Program Files (x86)\Git\bin', '') + os.environ['PATH'] = r'C:\MinGW\bin;' + path +else: + # Add MSBuild 14.0 to PATH as described in + # http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc. + os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path + if image == 'Visual Studio 2019': + generator = 'Visual Studio 16 2019' + if platform == 'x64': + cmake_command.extend(['-A', 'x64']) + else: + if image == 'Visual Studio 2015': + generator = 'Visual Studio 14 2015' + elif image == 'Visual Studio 2017': + generator = 'Visual Studio 15 2017' + if platform == 'x64': + generator += ' Win64' + cmake_command.append('-G' + generator) + build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4'] + test_command = ['ctest', '-C', config] + +check_call(cmake_command) +check_call(build_command) +check_call(test_command) diff --git a/src/fmt/support/appveyor.yml b/src/fmt/support/appveyor.yml new file mode 100644 index 000000000..f53e43839 --- /dev/null +++ b/src/fmt/support/appveyor.yml @@ -0,0 +1,41 @@ +configuration: + - Debug + - Release + +clone_depth: 1 + +image: + - Visual Studio 2015 + - Visual Studio 2019 + - Visual Studio 2017 + +platform: + - Win32 + - x64 + +environment: + CTEST_OUTPUT_ON_FAILURE: 1 + MSVC_DEFAULT_OPTIONS: ON + BUILD: msvc + +matrix: + exclude: + - image: Visual Studio 2015 + platform: Win32 + - image: Visual Studio 2019 + platform: Win32 + +before_build: + - mkdir build + - cd build + +build_script: + - python ../support/appveyor-build.py + +on_failure: + - appveyor PushArtifact Testing/Temporary/LastTest.log + - appveyor AddTest test + +# Uncomment this to debug AppVeyor failures. +#on_finish: +# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/src/fmt/support/build.gradle b/src/fmt/support/build.gradle new file mode 100644 index 000000000..11648da88 --- /dev/null +++ b/src/fmt/support/build.gradle @@ -0,0 +1,107 @@ + +// General gradle arguments for root project +buildscript { + repositories { + google() + jcenter() + } + dependencies { + // + // https://developer.android.com/studio/releases/gradle-plugin + // + // Notice that 3.3.0 here is the version of [Android Gradle Plugin] + // Accroding to URL above you will need Gradle 5.0 or higher + // + // If you are using Android Studio, and it is using Gradle's lower + // version, Use the plugin version 3.1.3 ~ 3.2.0 for Gradle 4.4 ~ 4.10 + classpath 'com.android.tools.build:gradle:3.3.0' + } +} +repositories { + google() + jcenter() +} + +// Output: Shared library (.so) for Android +apply plugin: 'com.android.library' + +android { + compileSdkVersion 25 // Android 7.0 + + // Target ABI + // - This option controls target platform of module + // - The platform might be limited by compiler's support + // some can work with Clang(default), but some can work only with GCC... + // if bad, both toolchains might not support it + splits { + abi { + enable true + // Specify platforms for Application + reset() + include "arm64-v8a", "armeabi-v7a", "x86_64" + } + } + + defaultConfig { + minSdkVersion 21 // Android 5.0+ + targetSdkVersion 25 // Follow Compile SDK + versionCode 21 // Follow release count + versionName "5.3.0" // Follow Official version + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + externalNativeBuild { + cmake { + arguments "-DANDROID_STL=c++_shared" // Specify Android STL + arguments "-DBUILD_SHARED_LIBS=true" // Build shared object + arguments "-DFMT_TEST=false" // Skip test + arguments "-DFMT_DOC=false" // Skip document + cppFlags "-std=c++17" + } + } + println("Gradle CMake Plugin: ") + println(externalNativeBuild.cmake.cppFlags) + println(externalNativeBuild.cmake.arguments) + } + + // External Native build + // - Use existing CMakeList.txt + // - Give path to CMake. This gradle file should be + // neighbor of the top level cmake + externalNativeBuild { + cmake { + path "../CMakeLists.txt" + // buildStagingDirectory "./build" // Custom path for cmake output + } + //println(cmake.path) + } + + sourceSets{ + // Android Manifest for Gradle + main { + manifest.srcFile 'AndroidManifest.xml' + } + } +} + +assemble.doLast +{ + // Instead of `ninja install`, Gradle will deploy the files. + // We are doing this since FMT is dependent to the ANDROID_STL after build + copy { + from 'build/intermediates/cmake' + into '../libs' + } + // Copy debug binaries + copy { + from '../libs/debug/obj' + into '../libs/debug' + } + // Copy Release binaries + copy { + from '../libs/release/obj' + into '../libs/release' + } + // Remove empty directory + delete '../libs/debug/obj' + delete '../libs/release/obj' +} diff --git a/src/fmt/support/cmake/FindSetEnv.cmake b/src/fmt/support/cmake/FindSetEnv.cmake new file mode 100644 index 000000000..4e2da5408 --- /dev/null +++ b/src/fmt/support/cmake/FindSetEnv.cmake @@ -0,0 +1,7 @@ +# A CMake script to find SetEnv.cmd. + +find_program(WINSDK_SETENV NAMES SetEnv.cmd + PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]/bin") +if (WINSDK_SETENV AND PRINT_PATH) + execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${WINSDK_SETENV}") +endif () diff --git a/src/fmt/support/cmake/JoinPaths.cmake b/src/fmt/support/cmake/JoinPaths.cmake new file mode 100644 index 000000000..32d6d6685 --- /dev/null +++ b/src/fmt/support/cmake/JoinPaths.cmake @@ -0,0 +1,26 @@ +# This module provides function for joining paths +# known from from most languages +# +# Original license: +# SPDX-License-Identifier: (MIT OR CC0-1.0) +# Explicit permission given to distribute this module under +# the terms of the project as described in /LICENSE.rst. +# Copyright 2020 Jan Tojnar +# https://github.com/jtojnar/cmake-snips +# +# Modelled after Python’s os.path.join +# https://docs.python.org/3.7/library/os.path.html#os.path.join +# Windows not supported +function(join_paths joined_path first_path_segment) + set(temp_path "${first_path_segment}") + foreach(current_segment IN LISTS ARGN) + if(NOT ("${current_segment}" STREQUAL "")) + if(IS_ABSOLUTE "${current_segment}") + set(temp_path "${current_segment}") + else() + set(temp_path "${temp_path}/${current_segment}") + endif() + endif() + endforeach() + set(${joined_path} "${temp_path}" PARENT_SCOPE) +endfunction() diff --git a/src/fmt/support/cmake/cxx14.cmake b/src/fmt/support/cmake/cxx14.cmake new file mode 100644 index 000000000..16ff57541 --- /dev/null +++ b/src/fmt/support/cmake/cxx14.cmake @@ -0,0 +1,70 @@ +# C++14 feature support detection + +include(CheckCXXSourceCompiles) +include(CheckCXXCompilerFlag) + +if (NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 11) +endif() +message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}") + +if (CMAKE_CXX_STANDARD EQUAL 20) + check_cxx_compiler_flag(-std=c++20 has_std_20_flag) + check_cxx_compiler_flag(-std=c++2a has_std_2a_flag) + + if (has_std_20_flag) + set(CXX_STANDARD_FLAG -std=c++20) + elseif (has_std_2a_flag) + set(CXX_STANDARD_FLAG -std=c++2a) + endif () +elseif (CMAKE_CXX_STANDARD EQUAL 17) + check_cxx_compiler_flag(-std=c++17 has_std_17_flag) + check_cxx_compiler_flag(-std=c++1z has_std_1z_flag) + + if (has_std_17_flag) + set(CXX_STANDARD_FLAG -std=c++17) + elseif (has_std_1z_flag) + set(CXX_STANDARD_FLAG -std=c++1z) + endif () +elseif (CMAKE_CXX_STANDARD EQUAL 14) + check_cxx_compiler_flag(-std=c++14 has_std_14_flag) + check_cxx_compiler_flag(-std=c++1y has_std_1y_flag) + + if (has_std_14_flag) + set(CXX_STANDARD_FLAG -std=c++14) + elseif (has_std_1y_flag) + set(CXX_STANDARD_FLAG -std=c++1y) + endif () +elseif (CMAKE_CXX_STANDARD EQUAL 11) + check_cxx_compiler_flag(-std=c++11 has_std_11_flag) + check_cxx_compiler_flag(-std=c++0x has_std_0x_flag) + + if (has_std_11_flag) + set(CXX_STANDARD_FLAG -std=c++11) + elseif (has_std_0x_flag) + set(CXX_STANDARD_FLAG -std=c++0x) + endif () +endif () + +set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG}) + +# Check if user-defined literals are available +check_cxx_source_compiles(" + void operator\"\" _udl(long double); + int main() {}" + SUPPORTS_USER_DEFINED_LITERALS) +if (NOT SUPPORTS_USER_DEFINED_LITERALS) + set (SUPPORTS_USER_DEFINED_LITERALS OFF) +endif () + +# Check if <variant> is available +set(CMAKE_REQUIRED_FLAGS -std=c++1z) +check_cxx_source_compiles(" + #include <variant> + int main() {}" + FMT_HAS_VARIANT) +if (NOT FMT_HAS_VARIANT) + set (FMT_HAS_VARIANT OFF) +endif () + +set(CMAKE_REQUIRED_FLAGS ) diff --git a/src/fmt/support/cmake/fmt-config.cmake.in b/src/fmt/support/cmake/fmt-config.cmake.in new file mode 100644 index 000000000..71e302860 --- /dev/null +++ b/src/fmt/support/cmake/fmt-config.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ + +include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) +check_required_components(fmt) diff --git a/src/fmt/support/cmake/fmt.pc.in b/src/fmt/support/cmake/fmt.pc.in new file mode 100644 index 000000000..29976a8af --- /dev/null +++ b/src/fmt/support/cmake/fmt.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@libdir_for_pc_file@ +includedir=@includedir_for_pc_file@ + +Name: fmt +Description: A modern formatting library +Version: @FMT_VERSION@ +Libs: -L${libdir} -l@FMT_LIB_NAME@ +Cflags: -I${includedir} + diff --git a/src/fmt/support/compute-powers.py b/src/fmt/support/compute-powers.py new file mode 100755 index 000000000..601063d42 --- /dev/null +++ b/src/fmt/support/compute-powers.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# Compute 10 ** exp with exp in the range [min_exponent, max_exponent] and print +# normalized (with most-significant bit equal to 1) significands in hexadecimal. + +from __future__ import print_function + +min_exponent = -348 +max_exponent = 340 +step = 8 +significand_size = 64 +exp_offset = 2000 + +class fp: + pass + +powers = [] +for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)): + result = fp() + n = 10 ** exp if exp >= 0 else 2 ** exp_offset / 10 ** -exp + k = significand_size + 1 + # Convert to binary and round. + binary = '{:b}'.format(n) + result.f = (int('{:0<{}}'.format(binary[:k], k), 2) + 1) / 2 + result.e = len(binary) - (exp_offset if exp < 0 else 0) - significand_size + powers.append(result) + # Sanity check. + exp_offset10 = 400 + actual = result.f * 10 ** exp_offset10 + if result.e > 0: + actual *= 2 ** result.e + else: + for j in range(-result.e): + actual /= 2 + expected = 10 ** (exp_offset10 + exp) + precision = len('{}'.format(expected)) - len('{}'.format(actual - expected)) + if precision < 19: + print('low precision:', precision) + exit(1) + +print('Significands:', end='') +for i, fp in enumerate(powers): + if i % 3 == 0: + print(end='\n ') + print(' {:0<#16x}'.format(fp.f, ), end=',') + +print('\n\nExponents:', end='') +for i, fp in enumerate(powers): + if i % 11 == 0: + print(end='\n ') + print(' {:5}'.format(fp.e), end=',') + +print('\n\nMax exponent difference:', + max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:])) diff --git a/src/fmt/support/docopt.py b/src/fmt/support/docopt.py new file mode 100644 index 000000000..2e43f7cef --- /dev/null +++ b/src/fmt/support/docopt.py @@ -0,0 +1,581 @@ +"""Pythonic command-line interface parser that will make you smile. + + * http://docopt.org + * Repository and issue-tracker: https://github.com/docopt/docopt + * Licensed under terms of MIT license (see LICENSE-MIT) + * Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com + +""" +import sys +import re + + +__all__ = ['docopt'] +__version__ = '0.6.1' + + +class DocoptLanguageError(Exception): + + """Error in construction of usage-message by developer.""" + + +class DocoptExit(SystemExit): + + """Exit in case user invoked program with incorrect arguments.""" + + usage = '' + + def __init__(self, message=''): + SystemExit.__init__(self, (message + '\n' + self.usage).strip()) + + +class Pattern(object): + + def __eq__(self, other): + return repr(self) == repr(other) + + def __hash__(self): + return hash(repr(self)) + + def fix(self): + self.fix_identities() + self.fix_repeating_arguments() + return self + + def fix_identities(self, uniq=None): + """Make pattern-tree tips point to same object if they are equal.""" + if not hasattr(self, 'children'): + return self + uniq = list(set(self.flat())) if uniq is None else uniq + for i, child in enumerate(self.children): + if not hasattr(child, 'children'): + assert child in uniq + self.children[i] = uniq[uniq.index(child)] + else: + child.fix_identities(uniq) + + def fix_repeating_arguments(self): + """Fix elements that should accumulate/increment values.""" + either = [list(child.children) for child in transform(self).children] + for case in either: + for e in [child for child in case if case.count(child) > 1]: + if type(e) is Argument or type(e) is Option and e.argcount: + if e.value is None: + e.value = [] + elif type(e.value) is not list: + e.value = e.value.split() + if type(e) is Command or type(e) is Option and e.argcount == 0: + e.value = 0 + return self + + +def transform(pattern): + """Expand pattern into an (almost) equivalent one, but with single Either. + + Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) + Quirks: [-a] => (-a), (-a...) => (-a -a) + + """ + result = [] + groups = [[pattern]] + while groups: + children = groups.pop(0) + parents = [Required, Optional, OptionsShortcut, Either, OneOrMore] + if any(t in map(type, children) for t in parents): + child = [c for c in children if type(c) in parents][0] + children.remove(child) + if type(child) is Either: + for c in child.children: + groups.append([c] + children) + elif type(child) is OneOrMore: + groups.append(child.children * 2 + children) + else: + groups.append(child.children + children) + else: + result.append(children) + return Either(*[Required(*e) for e in result]) + + +class LeafPattern(Pattern): + + """Leaf/terminal node of a pattern tree.""" + + def __init__(self, name, value=None): + self.name, self.value = name, value + + def __repr__(self): + return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value) + + def flat(self, *types): + return [self] if not types or type(self) in types else [] + + def match(self, left, collected=None): + collected = [] if collected is None else collected + pos, match = self.single_match(left) + if match is None: + return False, left, collected + left_ = left[:pos] + left[pos + 1:] + same_name = [a for a in collected if a.name == self.name] + if type(self.value) in (int, list): + if type(self.value) is int: + increment = 1 + else: + increment = ([match.value] if type(match.value) is str + else match.value) + if not same_name: + match.value = increment + return True, left_, collected + [match] + same_name[0].value += increment + return True, left_, collected + return True, left_, collected + [match] + + +class BranchPattern(Pattern): + + """Branch/inner node of a pattern tree.""" + + def __init__(self, *children): + self.children = list(children) + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, + ', '.join(repr(a) for a in self.children)) + + def flat(self, *types): + if type(self) in types: + return [self] + return sum([child.flat(*types) for child in self.children], []) + + +class Argument(LeafPattern): + + def single_match(self, left): + for n, pattern in enumerate(left): + if type(pattern) is Argument: + return n, Argument(self.name, pattern.value) + return None, None + + @classmethod + def parse(class_, source): + name = re.findall('(<\S*?>)', source)[0] + value = re.findall('\[default: (.*)\]', source, flags=re.I) + return class_(name, value[0] if value else None) + + +class Command(Argument): + + def __init__(self, name, value=False): + self.name, self.value = name, value + + def single_match(self, left): + for n, pattern in enumerate(left): + if type(pattern) is Argument: + if pattern.value == self.name: + return n, Command(self.name, True) + else: + break + return None, None + + +class Option(LeafPattern): + + def __init__(self, short=None, long=None, argcount=0, value=False): + assert argcount in (0, 1) + self.short, self.long, self.argcount = short, long, argcount + self.value = None if value is False and argcount else value + + @classmethod + def parse(class_, option_description): + short, long, argcount, value = None, None, 0, False + options, _, description = option_description.strip().partition(' ') + options = options.replace(',', ' ').replace('=', ' ') + for s in options.split(): + if s.startswith('--'): + long = s + elif s.startswith('-'): + short = s + else: + argcount = 1 + if argcount: + matched = re.findall('\[default: (.*)\]', description, flags=re.I) + value = matched[0] if matched else None + return class_(short, long, argcount, value) + + def single_match(self, left): + for n, pattern in enumerate(left): + if self.name == pattern.name: + return n, pattern + return None, None + + @property + def name(self): + return self.long or self.short + + def __repr__(self): + return 'Option(%r, %r, %r, %r)' % (self.short, self.long, + self.argcount, self.value) + + +class Required(BranchPattern): + + def match(self, left, collected=None): + collected = [] if collected is None else collected + l = left + c = collected + for pattern in self.children: + matched, l, c = pattern.match(l, c) + if not matched: + return False, left, collected + return True, l, c + + +class Optional(BranchPattern): + + def match(self, left, collected=None): + collected = [] if collected is None else collected + for pattern in self.children: + m, left, collected = pattern.match(left, collected) + return True, left, collected + + +class OptionsShortcut(Optional): + + """Marker/placeholder for [options] shortcut.""" + + +class OneOrMore(BranchPattern): + + def match(self, left, collected=None): + assert len(self.children) == 1 + collected = [] if collected is None else collected + l = left + c = collected + l_ = None + matched = True + times = 0 + while matched: + # could it be that something didn't match but changed l or c? + matched, l, c = self.children[0].match(l, c) + times += 1 if matched else 0 + if l_ == l: + break + l_ = l + if times >= 1: + return True, l, c + return False, left, collected + + +class Either(BranchPattern): + + def match(self, left, collected=None): + collected = [] if collected is None else collected + outcomes = [] + for pattern in self.children: + matched, _, _ = outcome = pattern.match(left, collected) + if matched: + outcomes.append(outcome) + if outcomes: + return min(outcomes, key=lambda outcome: len(outcome[1])) + return False, left, collected + + +class Tokens(list): + + def __init__(self, source, error=DocoptExit): + self += source.split() if hasattr(source, 'split') else source + self.error = error + + @staticmethod + def from_pattern(source): + source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source) + source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s] + return Tokens(source, error=DocoptLanguageError) + + def move(self): + return self.pop(0) if len(self) else None + + def current(self): + return self[0] if len(self) else None + + +def parse_long(tokens, options): + """long ::= '--' chars [ ( ' ' | '=' ) chars ] ;""" + long, eq, value = tokens.move().partition('=') + assert long.startswith('--') + value = None if eq == value == '' else value + similar = [o for o in options if o.long == long] + if tokens.error is DocoptExit and similar == []: # if no exact match + similar = [o for o in options if o.long and o.long.startswith(long)] + if len(similar) > 1: # might be simply specified ambiguously 2+ times? + raise tokens.error('%s is not a unique prefix: %s?' % + (long, ', '.join(o.long for o in similar))) + elif len(similar) < 1: + argcount = 1 if eq == '=' else 0 + o = Option(None, long, argcount) + options.append(o) + if tokens.error is DocoptExit: + o = Option(None, long, argcount, value if argcount else True) + else: + o = Option(similar[0].short, similar[0].long, + similar[0].argcount, similar[0].value) + if o.argcount == 0: + if value is not None: + raise tokens.error('%s must not have an argument' % o.long) + else: + if value is None: + if tokens.current() in [None, '--']: + raise tokens.error('%s requires argument' % o.long) + value = tokens.move() + if tokens.error is DocoptExit: + o.value = value if value is not None else True + return [o] + + +def parse_shorts(tokens, options): + """shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;""" + token = tokens.move() + assert token.startswith('-') and not token.startswith('--') + left = token.lstrip('-') + parsed = [] + while left != '': + short, left = '-' + left[0], left[1:] + similar = [o for o in options if o.short == short] + if len(similar) > 1: + raise tokens.error('%s is specified ambiguously %d times' % + (short, len(similar))) + elif len(similar) < 1: + o = Option(short, None, 0) + options.append(o) + if tokens.error is DocoptExit: + o = Option(short, None, 0, True) + else: # why copying is necessary here? + o = Option(short, similar[0].long, + similar[0].argcount, similar[0].value) + value = None + if o.argcount != 0: + if left == '': + if tokens.current() in [None, '--']: + raise tokens.error('%s requires argument' % short) + value = tokens.move() + else: + value = left + left = '' + if tokens.error is DocoptExit: + o.value = value if value is not None else True + parsed.append(o) + return parsed + + +def parse_pattern(source, options): + tokens = Tokens.from_pattern(source) + result = parse_expr(tokens, options) + if tokens.current() is not None: + raise tokens.error('unexpected ending: %r' % ' '.join(tokens)) + return Required(*result) + + +def parse_expr(tokens, options): + """expr ::= seq ( '|' seq )* ;""" + seq = parse_seq(tokens, options) + if tokens.current() != '|': + return seq + result = [Required(*seq)] if len(seq) > 1 else seq + while tokens.current() == '|': + tokens.move() + seq = parse_seq(tokens, options) + result += [Required(*seq)] if len(seq) > 1 else seq + return [Either(*result)] if len(result) > 1 else result + + +def parse_seq(tokens, options): + """seq ::= ( atom [ '...' ] )* ;""" + result = [] + while tokens.current() not in [None, ']', ')', '|']: + atom = parse_atom(tokens, options) + if tokens.current() == '...': + atom = [OneOrMore(*atom)] + tokens.move() + result += atom + return result + + +def parse_atom(tokens, options): + """atom ::= '(' expr ')' | '[' expr ']' | 'options' + | long | shorts | argument | command ; + """ + token = tokens.current() + result = [] + if token in '([': + tokens.move() + matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token] + result = pattern(*parse_expr(tokens, options)) + if tokens.move() != matching: + raise tokens.error("unmatched '%s'" % token) + return [result] + elif token == 'options': + tokens.move() + return [OptionsShortcut()] + elif token.startswith('--') and token != '--': + return parse_long(tokens, options) + elif token.startswith('-') and token not in ('-', '--'): + return parse_shorts(tokens, options) + elif token.startswith('<') and token.endswith('>') or token.isupper(): + return [Argument(tokens.move())] + else: + return [Command(tokens.move())] + + +def parse_argv(tokens, options, options_first=False): + """Parse command-line argument vector. + + If options_first: + argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ; + else: + argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ; + + """ + parsed = [] + while tokens.current() is not None: + if tokens.current() == '--': + return parsed + [Argument(None, v) for v in tokens] + elif tokens.current().startswith('--'): + parsed += parse_long(tokens, options) + elif tokens.current().startswith('-') and tokens.current() != '-': + parsed += parse_shorts(tokens, options) + elif options_first: + return parsed + [Argument(None, v) for v in tokens] + else: + parsed.append(Argument(None, tokens.move())) + return parsed + + +def parse_defaults(doc): + defaults = [] + for s in parse_section('options:', doc): + # FIXME corner case "bla: options: --foo" + _, _, s = s.partition(':') # get rid of "options:" + split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:] + split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])] + options = [Option.parse(s) for s in split if s.startswith('-')] + defaults += options + return defaults + + +def parse_section(name, source): + pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)', + re.IGNORECASE | re.MULTILINE) + return [s.strip() for s in pattern.findall(source)] + + +def formal_usage(section): + _, _, section = section.partition(':') # drop "usage:" + pu = section.split() + return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )' + + +def extras(help, version, options, doc): + if help and any((o.name in ('-h', '--help')) and o.value for o in options): + print(doc.strip("\n")) + sys.exit() + if version and any(o.name == '--version' and o.value for o in options): + print(version) + sys.exit() + + +class Dict(dict): + def __repr__(self): + return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items())) + + +def docopt(doc, argv=None, help=True, version=None, options_first=False): + """Parse `argv` based on command-line interface described in `doc`. + + `docopt` creates your command-line interface based on its + description that you pass as `doc`. Such description can contain + --options, <positional-argument>, commands, which could be + [optional], (required), (mutually | exclusive) or repeated... + + Parameters + ---------- + doc : str + Description of your command-line interface. + argv : list of str, optional + Argument vector to be parsed. sys.argv[1:] is used if not + provided. + help : bool (default: True) + Set to False to disable automatic help on -h or --help + options. + version : any object + If passed, the object will be printed if --version is in + `argv`. + options_first : bool (default: False) + Set to True to require options precede positional arguments, + i.e. to forbid options and positional arguments intermix. + + Returns + ------- + args : dict + A dictionary, where keys are names of command-line elements + such as e.g. "--verbose" and "<path>", and values are the + parsed values of those elements. + + Example + ------- + >>> from docopt import docopt + >>> doc = ''' + ... Usage: + ... my_program tcp <host> <port> [--timeout=<seconds>] + ... my_program serial <port> [--baud=<n>] [--timeout=<seconds>] + ... my_program (-h | --help | --version) + ... + ... Options: + ... -h, --help Show this screen and exit. + ... --baud=<n> Baudrate [default: 9600] + ... ''' + >>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30'] + >>> docopt(doc, argv) + {'--baud': '9600', + '--help': False, + '--timeout': '30', + '--version': False, + '<host>': '127.0.0.1', + '<port>': '80', + 'serial': False, + 'tcp': True} + + See also + -------- + * For video introduction see http://docopt.org + * Full documentation is available in README.rst as well as online + at https://github.com/docopt/docopt#readme + + """ + argv = sys.argv[1:] if argv is None else argv + + usage_sections = parse_section('usage:', doc) + if len(usage_sections) == 0: + raise DocoptLanguageError('"usage:" (case-insensitive) not found.') + if len(usage_sections) > 1: + raise DocoptLanguageError('More than one "usage:" (case-insensitive).') + DocoptExit.usage = usage_sections[0] + + options = parse_defaults(doc) + pattern = parse_pattern(formal_usage(DocoptExit.usage), options) + # [default] syntax for argument is disabled + #for a in pattern.flat(Argument): + # same_name = [d for d in arguments if d.name == a.name] + # if same_name: + # a.value = same_name[0].value + argv = parse_argv(Tokens(argv), list(options), options_first) + pattern_options = set(pattern.flat(Option)) + for options_shortcut in pattern.flat(OptionsShortcut): + doc_options = parse_defaults(doc) + options_shortcut.children = list(set(doc_options) - pattern_options) + #if any_options: + # options_shortcut.children += [Option(o.short, o.long, o.argcount) + # for o in argv if type(o) is Option] + extras(help, version, argv, doc) + matched, left, collected = pattern.fix().match(argv) + if matched and left == []: # better error message if left? + return Dict((a.name, a.value) for a in (pattern.flat() + collected)) + raise DocoptExit() diff --git a/src/fmt/support/fmt.pro b/src/fmt/support/fmt.pro new file mode 100644 index 000000000..c555d0b4a --- /dev/null +++ b/src/fmt/support/fmt.pro @@ -0,0 +1,27 @@ +# Staticlib configuration for qmake builds +# For some reason qmake 3.1 fails to identify source dependencies and excludes format.cc and printf.cc +# from compilation so it _MUST_ be called as qmake -nodepend +# A workaround is implemented below: a custom compiler is defined which does not track dependencies + +TEMPLATE = lib + +TARGET = fmt + +QMAKE_EXT_CPP = .cc + +CONFIG = staticlib warn_on c++11 + +FMT_SOURCES = \ + ../src/format.cc \ + ../src/posix.cc + +fmt.name = libfmt +fmt.input = FMT_SOURCES +fmt.output = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ +fmt.clean = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ +fmt.depends = ${QMAKE_FILE_IN} +# QMAKE_RUN_CXX will not be expanded +fmt.commands = $$QMAKE_CXX -c $$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_WARN_ON $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO $$QMAKE_CXXFLAGS_CXX11 ${QMAKE_FILE_IN} +fmt.variable_out = OBJECTS +fmt.CONFIG = no_dependencies no_link +QMAKE_EXTRA_COMPILERS += fmt diff --git a/src/fmt/support/manage.py b/src/fmt/support/manage.py new file mode 100755 index 000000000..acb0d3c57 --- /dev/null +++ b/src/fmt/support/manage.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python + +"""Manage site and releases. + +Usage: + manage.py release [<branch>] + manage.py site + +For the release command $FMT_TOKEN should contain a GitHub personal access token +obtained from https://github.com/settings/tokens. +""" + +from __future__ import print_function +import datetime, docopt, errno, fileinput, json, os +import re, requests, shutil, sys, tempfile +from contextlib import contextmanager +from distutils.version import LooseVersion +from subprocess import check_call + + +class Git: + def __init__(self, dir): + self.dir = dir + + def call(self, method, args, **kwargs): + return check_call(['git', method] + list(args), **kwargs) + + def add(self, *args): + return self.call('add', args, cwd=self.dir) + + def checkout(self, *args): + return self.call('checkout', args, cwd=self.dir) + + def clean(self, *args): + return self.call('clean', args, cwd=self.dir) + + def clone(self, *args): + return self.call('clone', list(args) + [self.dir]) + + def commit(self, *args): + return self.call('commit', args, cwd=self.dir) + + def pull(self, *args): + return self.call('pull', args, cwd=self.dir) + + def push(self, *args): + return self.call('push', args, cwd=self.dir) + + def reset(self, *args): + return self.call('reset', args, cwd=self.dir) + + def update(self, *args): + clone = not os.path.exists(self.dir) + if clone: + self.clone(*args) + return clone + + +def clean_checkout(repo, branch): + repo.clean('-f', '-d') + repo.reset('--hard') + repo.checkout(branch) + + +class Runner: + def __init__(self, cwd): + self.cwd = cwd + + def __call__(self, *args, **kwargs): + kwargs['cwd'] = kwargs.get('cwd', self.cwd) + check_call(args, **kwargs) + + +def create_build_env(): + """Create a build environment.""" + class Env: + pass + env = Env() + + # Import the documentation build module. + env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + sys.path.insert(0, os.path.join(env.fmt_dir, 'doc')) + import build + + env.build_dir = 'build' + env.versions = build.versions + + # Virtualenv and repos are cached to speed up builds. + build.create_build_env(os.path.join(env.build_dir, 'virtualenv')) + + env.fmt_repo = Git(os.path.join(env.build_dir, 'fmt')) + return env + + +@contextmanager +def rewrite(filename): + class Buffer: + pass + buffer = Buffer() + if not os.path.exists(filename): + buffer.data = '' + yield buffer + return + with open(filename) as f: + buffer.data = f.read() + yield buffer + with open(filename, 'w') as f: + f.write(buffer.data) + + +fmt_repo_url = 'git@github.com:fmtlib/fmt' + + +def update_site(env): + env.fmt_repo.update(fmt_repo_url) + + doc_repo = Git(os.path.join(env.build_dir, 'fmtlib.github.io')) + doc_repo.update('git@github.com:fmtlib/fmtlib.github.io') + + for version in env.versions: + clean_checkout(env.fmt_repo, version) + target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc') + # Remove the old theme. + for entry in os.listdir(target_doc_dir): + path = os.path.join(target_doc_dir, entry) + if os.path.isdir(path): + shutil.rmtree(path) + # Copy the new theme. + for entry in ['_static', '_templates', 'basic-bootstrap', 'bootstrap', + 'conf.py', 'fmt.less']: + src = os.path.join(env.fmt_dir, 'doc', entry) + dst = os.path.join(target_doc_dir, entry) + copy = shutil.copytree if os.path.isdir(src) else shutil.copyfile + copy(src, dst) + # Rename index to contents. + contents = os.path.join(target_doc_dir, 'contents.rst') + if not os.path.exists(contents): + os.rename(os.path.join(target_doc_dir, 'index.rst'), contents) + # Fix issues in reference.rst/api.rst. + for filename in ['reference.rst', 'api.rst', 'index.rst']: + pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M) + with rewrite(os.path.join(target_doc_dir, filename)) as b: + b.data = b.data.replace('std::ostream &', 'std::ostream&') + b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data) + b.data = b.data.replace('std::FILE*', 'std::FILE *') + b.data = b.data.replace('unsigned int', 'unsigned') + #b.data = b.data.replace('operator""_', 'operator"" _') + b.data = b.data.replace(', size_t', ', std::size_t') + b.data = b.data.replace('aa long', 'a long') + if version.startswith('6.2.'): + b.data = b.data.replace( + 'vformat(const S&, basic_format_args<' + + 'buffer_context<Char>>)', + 'vformat(const S&, basic_format_args<' + + 'buffer_context<type_identity_t<Char>>>)') + # Fix a broken link in index.rst. + index = os.path.join(target_doc_dir, 'index.rst') + with rewrite(index) as b: + b.data = b.data.replace( + 'doc/latest/index.html#format-string-syntax', 'syntax.html') + # Build the docs. + html_dir = os.path.join(env.build_dir, 'html') + if os.path.exists(html_dir): + shutil.rmtree(html_dir) + include_dir = env.fmt_repo.dir + if LooseVersion(version) >= LooseVersion('5.0.0'): + include_dir = os.path.join(include_dir, 'include', 'fmt') + elif LooseVersion(version) >= LooseVersion('3.0.0'): + include_dir = os.path.join(include_dir, 'fmt') + import build + build.build_docs(version, doc_dir=target_doc_dir, + include_dir=include_dir, work_dir=env.build_dir) + shutil.rmtree(os.path.join(html_dir, '.doctrees')) + # Create symlinks for older versions. + for link, target in {'index': 'contents', 'api': 'reference'}.items(): + link = os.path.join(html_dir, link) + '.html' + target += '.html' + if os.path.exists(os.path.join(html_dir, target)) and \ + not os.path.exists(link): + os.symlink(target, link) + # Copy docs to the website. + version_doc_dir = os.path.join(doc_repo.dir, version) + try: + shutil.rmtree(version_doc_dir) + except OSError as e: + if e.errno != errno.ENOENT: + raise + shutil.move(html_dir, version_doc_dir) + + +def release(args): + env = create_build_env() + fmt_repo = env.fmt_repo + + branch = args.get('<branch>') + if branch is None: + branch = 'master' + if not fmt_repo.update('-b', branch, fmt_repo_url): + clean_checkout(fmt_repo, branch) + + # Convert changelog from RST to GitHub-flavored Markdown and get the + # version. + changelog = 'ChangeLog.rst' + changelog_path = os.path.join(fmt_repo.dir, changelog) + import rst2md + changes, version = rst2md.convert(changelog_path) + cmakelists = 'CMakeLists.txt' + for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists), + inplace=True): + prefix = 'set(FMT_VERSION ' + if line.startswith(prefix): + line = prefix + version + ')\n' + sys.stdout.write(line) + + # Update the version in the changelog. + title_len = 0 + for line in fileinput.input(changelog_path, inplace=True): + if line.decode('utf-8').startswith(version + ' - TBD'): + line = version + ' - ' + datetime.date.today().isoformat() + title_len = len(line) + line += '\n' + elif title_len: + line = '-' * title_len + '\n' + title_len = 0 + sys.stdout.write(line) + + # Add the version to the build script. + script = os.path.join('doc', 'build.py') + script_path = os.path.join(fmt_repo.dir, script) + for line in fileinput.input(script_path, inplace=True): + m = re.match(r'( *versions = )\[(.+)\]', line) + if m: + line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version) + sys.stdout.write(line) + + fmt_repo.checkout('-B', 'release') + fmt_repo.add(changelog, cmakelists, script) + fmt_repo.commit('-m', 'Update version') + + # Build the docs and package. + run = Runner(fmt_repo.dir) + run('cmake', '.') + run('make', 'doc', 'package_source') + update_site(env) + + # Create a release on GitHub. + fmt_repo.push('origin', 'release') + params = {'access_token': os.getenv('FMT_TOKEN')} + r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases', + params=params, + data=json.dumps({'tag_name': version, + 'target_commitish': 'release', + 'body': changes, 'draft': True})) + if r.status_code != 201: + raise Exception('Failed to create a release ' + str(r)) + id = r.json()['id'] + uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases' + package = 'fmt-{}.zip'.format(version) + r = requests.post( + '{}/{}/assets?name={}'.format(uploads_url, id, package), + headers={'Content-Type': 'application/zip'}, + params=params, data=open('build/fmt/' + package, 'rb')) + if r.status_code != 201: + raise Exception('Failed to upload an asset ' + str(r)) + + +if __name__ == '__main__': + args = docopt.docopt(__doc__) + if args.get('release'): + release(args) + elif args.get('site'): + update_site(create_build_env()) diff --git a/src/fmt/support/rst2md.py b/src/fmt/support/rst2md.py new file mode 100755 index 000000000..cf170fe2b --- /dev/null +++ b/src/fmt/support/rst2md.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# reStructuredText (RST) to GitHub-flavored Markdown converter + +import re, sys +from docutils import core, nodes, writers + + +def is_github_ref(node): + return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri']) + + +class Translator(nodes.NodeVisitor): + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + self.output = '' + self.indent = 0 + self.preserve_newlines = False + + def write(self, text): + self.output += text.replace('\n', '\n' + ' ' * self.indent) + + def visit_document(self, node): + pass + + def depart_document(self, node): + pass + + def visit_section(self, node): + pass + + def depart_section(self, node): + # Skip all sections except the first one. + raise nodes.StopTraversal + + def visit_title(self, node): + self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1) + raise nodes.SkipChildren + + def visit_title_reference(self, node): + raise Exception(node) + + def depart_title(self, node): + pass + + def visit_Text(self, node): + if not self.preserve_newlines: + node = node.replace('\n', ' ') + self.write(node) + + def depart_Text(self, node): + pass + + def visit_bullet_list(self, node): + pass + + def depart_bullet_list(self, node): + pass + + def visit_list_item(self, node): + self.write('* ') + self.indent += 2 + + def depart_list_item(self, node): + self.indent -= 2 + self.write('\n\n') + + def visit_paragraph(self, node): + pass + + def depart_paragraph(self, node): + pass + + def visit_reference(self, node): + if not is_github_ref(node): + self.write('[') + + def depart_reference(self, node): + if not is_github_ref(node): + self.write('](' + node['refuri'] + ')') + + def visit_target(self, node): + pass + + def depart_target(self, node): + pass + + def visit_literal(self, node): + self.write('`') + + def depart_literal(self, node): + self.write('`') + + def visit_literal_block(self, node): + self.write('\n\n```') + if 'c++' in node['classes']: + self.write('c++') + self.write('\n') + self.preserve_newlines = True + + def depart_literal_block(self, node): + self.write('\n```\n') + self.preserve_newlines = False + + def visit_inline(self, node): + pass + + def depart_inline(self, node): + pass + + def visit_image(self, node): + self.write('![](' + node['uri'] + ')') + + def depart_image(self, node): + pass + + def write_row(self, row, widths): + for i, entry in enumerate(row): + text = entry[0][0] if len(entry) > 0 else '' + if i != 0: + self.write('|') + self.write('{:{}}'.format(text, widths[i])) + self.write('\n') + + def visit_table(self, node): + table = node.children[0] + colspecs = table[:-2] + thead = table[-2] + tbody = table[-1] + widths = [int(cs['colwidth']) for cs in colspecs] + sep = '|'.join(['-' * w for w in widths]) + '\n' + self.write('\n\n') + self.write_row(thead[0], widths) + self.write(sep) + for row in tbody: + self.write_row(row, widths) + raise nodes.SkipChildren + + def depart_table(self, node): + pass + +class MDWriter(writers.Writer): + """GitHub-flavored markdown writer""" + + supported = ('md',) + """Formats this writer supports.""" + + def translate(self): + translator = Translator(self.document) + self.document.walkabout(translator) + self.output = (translator.output, translator.version) + + +def convert(rst_path): + """Converts RST file to Markdown.""" + return core.publish_file(source_path=rst_path, writer=MDWriter()) + + +if __name__ == '__main__': + convert(sys.argv[1]) diff --git a/src/fmt/support/rtd/conf.py b/src/fmt/support/rtd/conf.py new file mode 100644 index 000000000..124fb9d4c --- /dev/null +++ b/src/fmt/support/rtd/conf.py @@ -0,0 +1,7 @@ +# Sphinx configuration for readthedocs. + +import os, sys + +master_doc = 'index' +html_theme = 'theme' +html_theme_path = ["."] diff --git a/src/fmt/support/rtd/index.rst b/src/fmt/support/rtd/index.rst new file mode 100644 index 000000000..7c88322f6 --- /dev/null +++ b/src/fmt/support/rtd/index.rst @@ -0,0 +1,2 @@ +If you are not redirected automatically, follow the +`link to the fmt documentation <https://fmt.dev/latest/>`_. diff --git a/src/fmt/support/rtd/theme/layout.html b/src/fmt/support/rtd/theme/layout.html new file mode 100644 index 000000000..29ebc55bb --- /dev/null +++ b/src/fmt/support/rtd/theme/layout.html @@ -0,0 +1,17 @@ +{% extends "basic/layout.html" %} + +{% block extrahead %} +<meta charset="UTF-8"> +<meta http-equiv="refresh" content="1;url=https://fmt.dev/latest/"> +<script type="text/javascript"> + window.location.href = "https://fmt.dev/latest/" +</script> +<title>Page Redirection</title> +{% endblock %} + +{% block document %} +If you are not redirected automatically, follow the <a href='https://fmt.dev/latest/'>link to the fmt documentation</a>. +{% endblock %} + +{% block footer %} +{% endblock %} diff --git a/src/fmt/support/rtd/theme/theme.conf b/src/fmt/support/rtd/theme/theme.conf new file mode 100644 index 000000000..89e03bbda --- /dev/null +++ b/src/fmt/support/rtd/theme/theme.conf @@ -0,0 +1,2 @@ +[theme] +inherit = basic diff --git a/src/fmt/support/travis-build.py b/src/fmt/support/travis-build.py new file mode 100755 index 000000000..0cf73c846 --- /dev/null +++ b/src/fmt/support/travis-build.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# Build the project on Travis CI. + +from __future__ import print_function +import errno, os, shutil, subprocess, sys, urllib +from subprocess import call, check_call, Popen, PIPE, STDOUT + +def rmtree_if_exists(dir): + try: + shutil.rmtree(dir) + except OSError as e: + if e.errno == errno.ENOENT: + pass + +def makedirs_if_not_exist(dir): + try: + os.makedirs(dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + +def install_dependencies(): + branch = os.environ['TRAVIS_BRANCH'] + if branch != 'master': + print('Branch: ' + branch) + exit(0) # Ignore non-master branches + check_call('curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key ' + + '| sudo apt-key add -', shell=True) + check_call('echo "deb https://deb.nodesource.com/node_0.10 precise main" ' + + '| sudo tee /etc/apt/sources.list.d/nodesource.list', shell=True) + check_call(['sudo', 'apt-get', 'update']) + check_call(['sudo', 'apt-get', 'install', 'python-virtualenv', 'nodejs']) + check_call(['sudo', 'npm', 'install', '-g', 'less@2.6.1', 'less-plugin-clean-css']) + deb_file = 'doxygen_1.8.6-2_amd64.deb' + urllib.urlretrieve('http://mirrors.kernel.org/ubuntu/pool/main/d/doxygen/' + + deb_file, deb_file) + check_call(['sudo', 'dpkg', '-i', deb_file]) + +fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + +build = os.environ['BUILD'] +if build == 'Doc': + travis = 'TRAVIS' in os.environ + if travis: + install_dependencies() + sys.path.insert(0, os.path.join(fmt_dir, 'doc')) + import build + build.create_build_env() + html_dir = build.build_docs() + repo = 'fmtlib.github.io' + if travis and 'KEY' not in os.environ: + # Don't update the repo if building on Travis from an account that + # doesn't have push access. + print('Skipping update of ' + repo) + exit(0) + # Clone the fmtlib.github.io repo. + rmtree_if_exists(repo) + git_url = 'https://github.com/' if travis else 'git@github.com:' + check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)]) + # Copy docs to the repo. + target_dir = os.path.join(repo, 'dev') + rmtree_if_exists(target_dir) + shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*')) + if travis: + check_call(['git', 'config', '--global', 'user.name', 'amplbot']) + check_call(['git', 'config', '--global', 'user.email', 'viz@ampl.com']) + # Push docs to GitHub pages. + check_call(['git', 'add', '--all'], cwd=repo) + if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo): + check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo) + cmd = 'git push' + if travis: + cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master' + p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo) + # Print the output without the key. + print(p.communicate()[0].replace(os.environ['KEY'], '$KEY')) + if p.returncode != 0: + raise subprocess.CalledProcessError(p.returncode, cmd) + exit(0) + +standard = os.environ['STANDARD'] +install_dir = os.path.join(fmt_dir, "_install") +build_dir = os.path.join(fmt_dir, "_build") +test_build_dir = os.path.join(fmt_dir, "_build_test") + +# Configure the library. +makedirs_if_not_exist(build_dir) +cmake_flags = [ + '-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build, + '-DCMAKE_CXX_STANDARD=' + standard +] + +# Make sure the fuzzers still compile. +main_cmake_flags = list(cmake_flags) +if 'ENABLE_FUZZING' in os.environ: + main_cmake_flags += ['-DFMT_FUZZ=ON', '-DFMT_FUZZ_LINKMAIN=On'] + +check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', '-DFMT_WERROR=ON', fmt_dir] + + main_cmake_flags, cwd=build_dir) + +# Build the library. +check_call(['cmake', '--build','.'], cwd=build_dir) + +# Test the library. +env = os.environ.copy() +env['CTEST_OUTPUT_ON_FAILURE'] = '1' +if call(['make', 'test'], env=env, cwd=build_dir): + with open(os.path.join(build_dir, 'Testing', 'Temporary', 'LastTest.log'), 'r') as f: + print(f.read()) + sys.exit(-1) + +# Install the library. +check_call(['make', 'install'], cwd=build_dir) + +# Test installation. +makedirs_if_not_exist(test_build_dir) +check_call(['cmake', os.path.join(fmt_dir, "test", "find-package-test")] + + cmake_flags, cwd=test_build_dir) +check_call(['make', '-j4'], cwd=test_build_dir) diff --git a/src/fmt/support/update-coverity-branch.py b/src/fmt/support/update-coverity-branch.py new file mode 100755 index 000000000..519f5d00d --- /dev/null +++ b/src/fmt/support/update-coverity-branch.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# Update the coverity branch from the master branch. +# It is not done automatically because Coverity Scan limits +# the number of submissions per day. + +from __future__ import print_function +import shutil, tempfile +from subprocess import check_output, STDOUT + +class Git: + def __init__(self, dir): + self.dir = dir + + def __call__(self, *args): + output = check_output(['git'] + list(args), cwd=self.dir, stderr=STDOUT) + print(output) + return output + +dir = tempfile.mkdtemp() +try: + git = Git(dir) + git('clone', '-b', 'coverity', 'git@github.com:fmtlib/fmt.git', dir) + output = git('merge', '-X', 'theirs', '--no-commit', 'origin/master') + if 'Fast-forward' not in output: + git('reset', 'HEAD', '.travis.yml') + git('checkout', '--', '.travis.yml') + git('commit', '-m', 'Update coverity branch') + git('push') +finally: + shutil.rmtree(dir) |