From bc54435b16267839c3e7dbbbbf2071450236dea3 Mon Sep 17 00:00:00 2001 From: Addison <152139745+addison-adler@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:47:41 -0500 Subject: [PATCH] [Proposal] Add Namespace Source Literals (#1354) * Add Namespace Source Literals * Update New Literal IDs with "_NAME" suffix * casing mismatch in Keywords consts * Snakecase hash strings must be assigned as strings --------- Co-authored-by: Addison Co-authored-by: Addison --- docs/source-literals.md | 44 ++++++++++++++++++++++++++++++++++++++++ src/astUtils/creators.ts | 2 ++ src/lexer/Lexer.spec.ts | 4 +++- src/lexer/TokenKind.ts | 10 +++++++++ src/parser/Expression.ts | 13 ++++++++++++ 5 files changed, 72 insertions(+), 1 deletion(-) diff --git a/docs/source-literals.md b/docs/source-literals.md index b29d73c1c..24e6d664a 100644 --- a/docs/source-literals.md +++ b/docs/source-literals.md @@ -142,6 +142,50 @@ function Human_Versus_Zombie_Eat() end function ``` +## SOURCE_NAMESPACE_NAME + +This works the same as SOURCE_FUNCTION_NAME except that it does not include the function name. + +The SOURCE_NAMESPACE_NAME can be a useful string for debug logs and logging filters. + +```BrighterScript +namespace Human.Versus.Zombie + function Eat() + print SOURCE_NAMESPACE_NAME + end function +end namespace +``` + +transpiles to: + +```BrightScript +function Human_Versus_Zombie_Eat() + print "Human.Versus.Zombie" +end function +``` + +## SOURCE_NAMESPACE_ROOT_NAME + +This works the same as SOURCE_NAMESPACE_NAME except that it only includes the lowest-level namespace. + +The SOURCE_NAMESPACE_ROOT_NAME can be a useful string for debug logs and logging filters. + +```BrighterScript +namespace Human.Versus.Zombie + function Eat() + print SOURCE_NAMESPACE_ROOT_NAME + end function +end namespace +``` + +transpiles to: + +```BrightScript +function Human_Versus_Zombie_Eat() + print "Human" +end function +``` + ## SOURCE_LOCATION A combination of SOURCE_FILE_PATH and SOURCE_LINE_NUM. diff --git a/src/astUtils/creators.ts b/src/astUtils/creators.ts index a36dc31b0..97906e2bc 100644 --- a/src/astUtils/creators.ts +++ b/src/astUtils/creators.ts @@ -74,6 +74,8 @@ const tokenDefaults = { [TokenKind.Semicolon]: ';', [TokenKind.SourceFilePathLiteral]: 'SOURCE_FILE_PATH', [TokenKind.SourceFunctionNameLiteral]: 'SOURCE_FUNCTION_NAME', + [TokenKind.SourceNamespaceRootNameLiteral]: 'SOURCE_NAMESPACE_ROOT_NAME', + [TokenKind.SourceNamespaceNameLiteral]: 'SOURCE_NAMESPACE_NAME', [TokenKind.SourceLineNumLiteral]: 'SOURCE_LINE_NUM', [TokenKind.SourceLocationLiteral]: 'SOURCE_LOCATION', [TokenKind.Star]: '*', diff --git a/src/lexer/Lexer.spec.ts b/src/lexer/Lexer.spec.ts index 1adddb923..6dfaf2c3f 100644 --- a/src/lexer/Lexer.spec.ts +++ b/src/lexer/Lexer.spec.ts @@ -1332,13 +1332,15 @@ describe('lexer', () => { }); it('identifies brighterscript source literals', () => { - let { tokens } = Lexer.scan('LINE_NUM SOURCE_FILE_PATH SOURCE_LINE_NUM FUNCTION_NAME SOURCE_FUNCTION_NAME SOURCE_LOCATION PKG_PATH PKG_LOCATION'); + let { tokens } = Lexer.scan('LINE_NUM SOURCE_FILE_PATH SOURCE_LINE_NUM FUNCTION_NAME SOURCE_FUNCTION_NAME SOURCE_NAMESPACE_NAME SOURCE_NAMESPACE_ROOT_NAME SOURCE_LOCATION PKG_PATH PKG_LOCATION'); expect(tokens.map(x => x.kind)).to.eql([ TokenKind.LineNumLiteral, TokenKind.SourceFilePathLiteral, TokenKind.SourceLineNumLiteral, TokenKind.FunctionNameLiteral, TokenKind.SourceFunctionNameLiteral, + TokenKind.SourceNamespaceNameLiteral, + TokenKind.SourceNamespaceRootNameLiteral, TokenKind.SourceLocationLiteral, TokenKind.PkgPathLiteral, TokenKind.PkgLocationLiteral, diff --git a/src/lexer/TokenKind.ts b/src/lexer/TokenKind.ts index a526af664..ae02fc1fe 100644 --- a/src/lexer/TokenKind.ts +++ b/src/lexer/TokenKind.ts @@ -170,6 +170,8 @@ export enum TokenKind { SourceLineNumLiteral = 'SourceLineNumLiteral', FunctionNameLiteral = 'FunctionNameLiteral', SourceFunctionNameLiteral = 'SourceFunctionNameLiteral', + SourceNamespaceNameLiteral = 'SourceNamespaceNameLiteral', + SourceNamespaceRootNameLiteral = 'SourceNamespaceRootNameLiteral', SourceLocationLiteral = 'SourceLocationLiteral', PkgPathLiteral = 'PkgPathLiteral', PkgLocationLiteral = 'PkgLocationLiteral', @@ -312,6 +314,8 @@ export const Keywords: Record = { 'source_line_num': TokenKind.SourceLineNumLiteral, 'function_name': TokenKind.FunctionNameLiteral, 'source_function_name': TokenKind.SourceFunctionNameLiteral, + 'source_namespace_name': TokenKind.SourceNamespaceNameLiteral, + 'source_namespace_root_name': TokenKind.SourceNamespaceRootNameLiteral, 'source_location': TokenKind.SourceLocationLiteral, 'pkg_path': TokenKind.PkgPathLiteral, 'pkg_location': TokenKind.PkgLocationLiteral, @@ -438,6 +442,8 @@ export const AllowedProperties = [ TokenKind.SourceLineNumLiteral, TokenKind.FunctionNameLiteral, TokenKind.SourceFunctionNameLiteral, + TokenKind.SourceNamespaceNameLiteral, + TokenKind.SourceNamespaceRootNameLiteral, TokenKind.SourceLocationLiteral, TokenKind.PkgPathLiteral, TokenKind.PkgLocationLiteral, @@ -490,6 +496,8 @@ export const BrighterScriptSourceLiterals = [ TokenKind.SourceLineNumLiteral, TokenKind.FunctionNameLiteral, TokenKind.SourceFunctionNameLiteral, + TokenKind.SourceNamespaceNameLiteral, + TokenKind.SourceNamespaceRootNameLiteral, TokenKind.SourceLocationLiteral, TokenKind.PkgPathLiteral, TokenKind.PkgLocationLiteral @@ -546,6 +554,8 @@ export const DisallowedLocalIdentifiers = [ TokenKind.SourceLineNumLiteral, TokenKind.FunctionNameLiteral, TokenKind.SourceFunctionNameLiteral, + TokenKind.SourceNamespaceNameLiteral, + TokenKind.SourceNamespaceRootNameLiteral, TokenKind.SourceLocationLiteral, TokenKind.PkgPathLiteral, TokenKind.PkgLocationLiteral, diff --git a/src/parser/Expression.ts b/src/parser/Expression.ts index 1eb3c706b..0c1561de9 100644 --- a/src/parser/Expression.ts +++ b/src/parser/Expression.ts @@ -1163,6 +1163,19 @@ export class SourceLiteralExpression extends Expression { case TokenKind.SourceFunctionNameLiteral: text = `"${this.getFunctionName(state, ParseMode.BrighterScript)}"`; break; + case TokenKind.SourceNamespaceNameLiteral: + let namespaceParts = this.getFunctionName(state, ParseMode.BrighterScript).split('.'); + namespaceParts.pop(); // remove the function name + + text = `"${namespaceParts.join('.')}"`; + break; + case TokenKind.SourceNamespaceRootNameLiteral: + let namespaceRootParts = this.getFunctionName(state, ParseMode.BrighterScript).split('.'); + namespaceRootParts.pop(); // remove the function name + + let rootNamespace = namespaceRootParts.shift() ?? ''; + text = `"${rootNamespace}"`; + break; case TokenKind.SourceLocationLiteral: const locationUrl = fileUrl(state.srcPath); //TODO find first parent that has range, or default to -1