diff --git a/Tmain/list-fields-with-prefix.d/stdout-expected.txt b/Tmain/list-fields-with-prefix.d/stdout-expected.txt index 1d953ac929..90b8c9ea30 100644 --- a/Tmain/list-fields-with-prefix.d/stdout-expected.txt +++ b/Tmain/list-fields-with-prefix.d/stdout-expected.txt @@ -32,6 +32,7 @@ x UCTAGSxpath no NONE s-- no -- xpath for - UCTAGSpackage yes Go s-- no -- the real package specified by the package name - UCTAGSpackageName yes Go s-- no -- the name for referring the package - UCTAGSimplements yes Inko s-- no rw Trait being implemented +- UCTAGSproperties no JavaScript s-- no -- properties (static) - UCTAGSassignment yes LdScript s-- no -- how a value is assigned to the symbol - UCTAGSdefiner yes Lisp s-- no -- the name of the function or macro that defines the unknown/Y-kind object - UCTAGSsectionMarker no Markdown s-- no -- character used for declaring section(#, ##, =, or -) diff --git a/Tmain/list-fields.d/stdout-expected.txt b/Tmain/list-fields.d/stdout-expected.txt index 2e3b11f194..b1f2acc328 100644 --- a/Tmain/list-fields.d/stdout-expected.txt +++ b/Tmain/list-fields.d/stdout-expected.txt @@ -50,6 +50,7 @@ z kind no NONE s-- no r- [tags output] prepend "kind:" to k/ (or K/) field outpu - package yes Go s-- no -- the real package specified by the package name - packageName yes Go s-- no -- the name for referring the package - implements yes Inko s-- no rw Trait being implemented +- properties no JavaScript s-- no -- properties (static) - assignment yes LdScript s-- no -- how a value is assigned to the symbol - definer yes Lisp s-- no -- the name of the function or macro that defines the unknown/Y-kind object - sectionMarker no Markdown s-- no -- character used for declaring section(#, ##, =, or -) diff --git a/Units/parser-javascript.r/js-es6-class-private.d/args.ctags b/Units/parser-javascript.r/js-es6-class-private.d/args.ctags new file mode 100644 index 0000000000..e015efa823 --- /dev/null +++ b/Units/parser-javascript.r/js-es6-class-private.d/args.ctags @@ -0,0 +1,3 @@ +--fields=+a +--fields-javascript=+{properties} +--sort=no diff --git a/Units/parser-javascript.r/js-es6-class-private.d/expected.tags b/Units/parser-javascript.r/js-es6-class-private.d/expected.tags new file mode 100644 index 0000000000..2ea557830f --- /dev/null +++ b/Units/parser-javascript.r/js-es6-class-private.d/expected.tags @@ -0,0 +1,25 @@ +Class1 input.js /^class Class1$/;" c +#value1 input.js /^ #value1$/;" M class:Class1 access:private +#value2 input.js /^ static #value2$/;" M class:Class1 access:private properties:static +method1 input.js /^ method1(arg1,arg2)$/;" m class:Class1 +#method2 input.js /^ #method2(arg1,arg2)$/;" m class:Class1 access:private +Class2 input.js /^class Class2$/;" c +#method3 input.js /^ static #method3(arg = 10)$/;" m class:Class2 access:private properties:static +Class3 input.js /^var Class3 = class {$/;" c +#method4 input.js /^ #method4(){}$/;" m class:Class3 access:private +method5 input.js /^ static method5(){}$/;" m class:Class3 properties:static +Class4 input.js /^var Class4 = class Class4_2 {$/;" c +Class4_2 input.js /^var Class4 = class Class4_2 {$/;" c +method6 input.js /^ method6(){}$/;" m class:Class4 +method7 input.js /^ static method7(){}$/;" m class:Class4 properties:static +AnonymousClass1fa6c9a30101 input.js /^class {$/;" c +method8 input.js /^ method8(n) { return n * n; }$/;" m class:AnonymousClass1fa6c9a30101 +func1 input.js /^function func1() {$/;" f +InnerClass1 input.js /^ class InnerClass1 {$/;" c function:func1 +#method9 input.js /^ #method9() {$/;" m class:func1.InnerClass1 access:private +InnerClass2 input.js /^ class InnerClass2 {$/;" c function:func1 +method10 input.js /^ method10() {$/;" m class:func1.InnerClass2 +Class5 input.js /^class Class5 {$/;" c +method11 input.js /^ method11() {};$/;" m class:Class5 +#method12 input.js /^ #method12() {};$/;" m class:Class5 access:private +func2 input.js /^function func2() {$/;" f diff --git a/Units/parser-javascript.r/js-es6-class-private.d/input.js b/Units/parser-javascript.r/js-es6-class-private.d/input.js new file mode 100644 index 0000000000..8a6bd06d46 --- /dev/null +++ b/Units/parser-javascript.r/js-es6-class-private.d/input.js @@ -0,0 +1,54 @@ + +class Class1 +{ + #value1 + static #value2 + + method1(arg1,arg2) + { + } + + #method2(arg1,arg2) + { + } +} + +class Class2 +{ + static #method3(arg = 10) + { + } +} + +var Class3 = class { + #method4(){} + static method5(){} +} + +var Class4 = class Class4_2 { + method6(){} + static method7(){} +} + +class { + method8(n) { return n * n; } +} + +function func1() { + class InnerClass1 { + #method9() { + } + } + class InnerClass2 { + method10() { + } + } +} + +class Class5 { + method11() {}; + #method12() {}; +} + +function func2() { +} diff --git a/docs/news/HEAD.rst b/docs/news/HEAD.rst index fabe65a4b3..b7d191ca08 100644 --- a/docs/news/HEAD.rst +++ b/docs/news/HEAD.rst @@ -14,6 +14,10 @@ Incompatible changes Parser related changes --------------------------------------------------------------------- +JavaScript: +* A new field "properties" was added to indicate that a field or + member of a class is static. +* Class member names prefixed with # are recognized as private. New parsers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/parsers/jscript.c b/parsers/jscript.c index d9cb6d07d2..18a8ab0a34 100644 --- a/parsers/jscript.c +++ b/parsers/jscript.c @@ -123,6 +123,7 @@ typedef enum eTokenType { TOKEN_REGEXP, TOKEN_POSTFIX_OPERATOR, TOKEN_STAR, + TOKEN_HASH, /* To handle Babel's decorators. * Used only in readTokenFull or lower functions. */ TOKEN_ATMARK, @@ -144,6 +145,10 @@ typedef struct sTokenInfo { int c; } tokenInfo; +typedef enum { + F_STATIC, +} jsField; + /* * DATA DEFINITIONS */ @@ -279,6 +284,14 @@ static kindDefinition JsKinds [] = { { true, 'M', "field", "fields" }, }; +static fieldDefinition JsFields[] = { + { + .name = "properties", + .description = "properties (static)", + .enabled = false, + }, +}; + static const keywordTable JsKeywordTable [] = { /* keyword keyword ID */ { "function", KEYWORD_function }, @@ -487,7 +500,7 @@ static int makeJsRefTagsForNameChain (char *name_chain, const tokenInfo *token, static int makeJsTagCommon (const tokenInfo *const token, const jsKind kind, vString *const signature, vString *const inheritance, - bool anonymous, bool nulltag) + bool anonymous, bool is_static, bool is_private, bool nulltag) { int index = CORK_NIL; const char *name = vStringValue (token->string); @@ -526,6 +539,12 @@ static int makeJsTagCommon (const tokenInfo *const token, const jsKind kind, updateTagLine (&e, token->lineNumber, token->filePosition); e.extensionFields.scopeIndex = scope; + if (is_private) + e.extensionFields.access = "private"; + + if (is_static) + attachParserField (&e, JsFields[F_STATIC].ftype, "static"); + #ifdef DO_TRACING { const char *scope_str = getNameStringForCorkIndex (scope); @@ -573,19 +592,33 @@ static int makeJsTagCommon (const tokenInfo *const token, const jsKind kind, static int makeJsTag (const tokenInfo *const token, const jsKind kind, vString *const signature, vString *const inheritance) { - return makeJsTagCommon (token, kind, signature, inheritance, false, false); + return makeJsTagCommon (token, kind, signature, inheritance, false, false, false, false); +} + +static int makeJsTagMaybePrivate (const tokenInfo *const token, const jsKind kind, + vString *const signature, vString *const inheritance, + const bool is_static, const bool is_private) +{ + if (is_private) { + vString * s = vStringNewInit ("#"); + vStringCat (s, token->string); + vStringCopy (token->string, s); + vStringDelete (s); + } + + return makeJsTagCommon (token, kind, signature, inheritance, false, is_static, is_private, false); } static int makeJsNullTag (const tokenInfo *const token, const jsKind kind, vString *const signature, vString *const inheritance) { - return makeJsTagCommon (token, kind, signature, inheritance, false, true); + return makeJsTagCommon (token, kind, signature, inheritance, false, false, false, true); } static int makeClassTagCommon (tokenInfo *const token, vString *const signature, vString *const inheritance, bool anonymous) { - return makeJsTagCommon (token, JSTAG_CLASS, signature, inheritance, anonymous, false); + return makeJsTagCommon (token, JSTAG_CLASS, signature, inheritance, anonymous, false, false, false); } static int makeClassTag (tokenInfo *const token, vString *const signature, @@ -598,7 +631,7 @@ static int makeFunctionTagCommon (tokenInfo *const token, vString *const signatu bool generator, bool anonymous) { return makeJsTagCommon (token, generator ? JSTAG_GENERATOR : JSTAG_FUNCTION, signature, NULL, - anonymous, false); + anonymous, false, false, false); } static int makeFunctionTag (tokenInfo *const token, vString *const signature, bool generator) @@ -1300,11 +1333,11 @@ static void readTokenFullRaw (tokenInfo *const token, bool include_newlines, vSt case '#': /* skip shebang in case of e.g. Node.js scripts */ if (token->lineNumber > 1) - token->type = TOKEN_UNDEFINED; + token->type = TOKEN_HASH; else if ((c = getcFromInputFile ()) != '!') { ungetcToInputFile (c); - token->type = TOKEN_UNDEFINED; + token->type = TOKEN_HASH; } else { @@ -1530,7 +1563,7 @@ static int parseMethodsInAnonymousObject (tokenInfo *const token) anonGenerate (anon_object->string, "anonymousObject", JSTAG_VARIABLE); anon_object->type = TOKEN_IDENTIFIER; - index = makeJsTagCommon (anon_object, JSTAG_VARIABLE, NULL, NULL, true, false); + index = makeJsTagCommon (anon_object, JSTAG_VARIABLE, NULL, NULL, true, false, false, false); if (! parseMethods (token, index, false)) { /* If no method is found, the anonymous object @@ -2162,6 +2195,7 @@ static bool parseMethods (tokenInfo *const token, int class_index, { bool is_setter = false; bool is_getter = false; + bool is_static = false; if (!dont_read) readToken (token); @@ -2204,6 +2238,9 @@ static bool parseMethods (tokenInfo *const token, int class_index, else if (isKeyword (saved_token, KEYWORD_async) || isKeyword (saved_token, KEYWORD_static)) { + if (isKeyword (saved_token, KEYWORD_static)) + is_static = true; + /* can be a qualifier for another "keyword", so start over */ deleteToken (saved_token); goto start; @@ -2222,6 +2259,7 @@ static bool parseMethods (tokenInfo *const token, int class_index, ! isType (token, TOKEN_SEMICOLON)) { bool is_generator = false; + bool is_private = false; bool is_shorthand = false; /* ES6 shorthand syntax */ bool is_computed_name = false; /* ES6 computed property name */ bool is_dynamic_prop = false; @@ -2235,6 +2273,11 @@ static bool parseMethods (tokenInfo *const token, int class_index, is_generator = true; readToken (token); } + else if (isType (token, TOKEN_HASH)) + { + is_private = true; + readToken (token); + } if (isType (token, TOKEN_OPEN_SQUARE)) { @@ -2352,7 +2395,7 @@ static bool parseMethods (tokenInfo *const token, int class_index, else if (is_setter) kind = JSTAG_SETTER; - index_for_name = makeJsTag (name, kind, signature, NULL); + index_for_name = makeJsTagMaybePrivate (name, kind, signature, NULL, is_static, is_private); parseBlock (token, index_for_name); /* @@ -2448,7 +2491,7 @@ static bool parseMethods (tokenInfo *const token, int class_index, else { bool is_property = isType (token, TOKEN_COMMA); - makeJsTag (name, is_property ? JSTAG_PROPERTY : JSTAG_FIELD, NULL, NULL); + makeJsTagMaybePrivate (name, is_property ? JSTAG_PROPERTY : JSTAG_FIELD, NULL, NULL, is_static, is_private); if (!isType (token, TOKEN_SEMICOLON) && !is_property) dont_read = true; } @@ -2517,7 +2560,7 @@ static bool parseES6Class (tokenInfo *const token, const tokenInfo *target_name) TRACE_PRINT("Emitting tag for class '%s'", vStringValue(target_name->string)); int r = makeJsTagCommon (target_name, JSTAG_CLASS, NULL, inheritance, - (is_anonymous && (target_name == class_name)), false); + (is_anonymous && (target_name == class_name)), false, false, false); if (! is_anonymous && target_name != class_name) { @@ -2855,7 +2898,7 @@ static bool parseStatementRHS (tokenInfo *const name, tokenInfo *const token, st if ( parseMethods(token, p, false) ) { jsKind kind = state->foundThis || strchr (vStringValue(name->string), '.') != NULL ? JSTAG_PROPERTY : JSTAG_VARIABLE; - state->indexForName = makeJsTagCommon (name, kind, NULL, NULL, anon_object, false); + state->indexForName = makeJsTagCommon (name, kind, NULL, NULL, anon_object, false, false, false); moveChildren (p, state->indexForName); } else if ( token->nestLevel == 0 && state->isGlobal ) @@ -2902,7 +2945,7 @@ static bool parseStatementRHS (tokenInfo *const name, tokenInfo *const token, st */ if ( ( token->nestLevel == 0 && state->isGlobal ) || kind == JSTAG_PROPERTY ) { - state->indexForName = makeJsTagCommon (name, kind, NULL, NULL, false, false); + state->indexForName = makeJsTagCommon (name, kind, NULL, NULL, false, false, false, false); } } else if (isKeyword (token, KEYWORD_new)) @@ -3762,6 +3805,8 @@ extern parserDefinition* JavaScriptParser (void) */ def->kindTable = JsKinds; def->kindCount = ARRAY_SIZE (JsKinds); + def->fieldTable = JsFields; + def->fieldCount = ARRAY_SIZE (JsFields); def->parser = findJsTags; def->initialize = initialize; def->finalize = finalize; @@ -3773,8 +3818,8 @@ extern parserDefinition* JavaScriptParser (void) def->dependencies = dependencies; def->dependencyCount = ARRAY_SIZE (dependencies); - def->versionCurrent = 1; - def->versionAge = 1; + def->versionCurrent = 2; + def->versionAge = 2; return def; }