From 5f4cf23a975cd2c9929c8c1d411904f1241e176c Mon Sep 17 00:00:00 2001 From: Markus Rudolph Date: Tue, 26 Nov 2024 08:59:09 +0100 Subject: [PATCH] Conflicting SymbolicLabelRef and multiplication #188 Fix the bug Add more escaped ids Added validation for symbolic label reference text #188 Rebased on master --- bbj-vscode/src/language/bbj-validator.ts | 9 ++++- bbj-vscode/src/language/bbj.langium | 42 ++++++++++++++++-------- bbj-vscode/test/parser.test.ts | 10 ++++++ bbj-vscode/test/validation.test.ts | 13 +++++++- examples/issue188-multiply.bbj | 3 ++ 5 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 examples/issue188-multiply.bbj diff --git a/bbj-vscode/src/language/bbj-validator.ts b/bbj-vscode/src/language/bbj-validator.ts index eebb054..275cbf2 100644 --- a/bbj-vscode/src/language/bbj-validator.ts +++ b/bbj-vscode/src/language/bbj-validator.ts @@ -8,7 +8,7 @@ import { AstNode, AstUtils, CompositeCstNode, CstNode, DiagnosticInfo, LeafCstNo import { dirname, isAbsolute, relative } from 'path'; import type { BBjServices } from './bbj-module.js'; import { TypeInferer } from './bbj-type-inferer.js'; -import { BBjAstType, Class, CommentStatement, DefFunction, EraseStatement, FieldDecl, InitFileStatement, JavaField, JavaMethod, KeyedFileStatement, MemberCall, MethodDecl, OpenStatement, Option, Use, isBBjClassMember, isBbjClass, isClass, isKeywordStatement, isLabelDecl, isOption } from './generated/ast.js'; +import { BBjAstType, Class, CommentStatement, DefFunction, EraseStatement, FieldDecl, InitFileStatement, JavaField, JavaMethod, KeyedFileStatement, MemberCall, MethodDecl, OpenStatement, Option, SymbolicLabelRef, Use, isBBjClassMember, isBbjClass, isClass, isKeywordStatement, isLabelDecl, isOption } from './generated/ast.js'; import { JavaInteropService } from './java-interop.js'; import { registerClassChecks } from './validations/check-classes.js'; import { checkLineBreaks, getPreviousNode } from './validations/line-break-validation.js'; @@ -30,6 +30,7 @@ export function registerValidationChecks(services: BBjServices) { CommentStatement: validator.checkCommentNewLines, MethodDecl: validator.checkIfMethodIsChildOfInterface, MemberCall: validator.checkMemberCallUsingAccessLevels, + SymbolicLabelRef: validator.checkSymbolicLabelRef }; registry.register(checks, validator); registerClassChecks(registry); @@ -226,6 +227,12 @@ export class BBjValidator { return; } } + + checkSymbolicLabelRef(ele: SymbolicLabelRef, accept: ValidationAcceptor): void { + if (ele.$cstNode?.text.search(/\s/) !== -1) { + accept('error', 'Symbolic label reference may not contain whitespace.', { node: ele }); + } + } } export function findLeafNodeAtOffset(node: CstNode, offset: number): LeafCstNode | undefined { diff --git a/bbj-vscode/src/language/bbj.langium b/bbj-vscode/src/language/bbj.langium index a3d8dff..6938d12 100644 --- a/bbj-vscode/src/language/bbj.langium +++ b/bbj-vscode/src/language/bbj.langium @@ -426,7 +426,7 @@ UserLabelRef infers LabelRef: ; SymbolicLabelRef infers LabelRef: - {infer SymbolicLabelRef} label=[LibSymbolicLabelDecl:SYMBOLIC_LABEL_NAME] + {infer SymbolicLabelRef} label=[LibSymbolicLabelDecl:SymbolicLabelName] ; KeywordStatement: @@ -709,24 +709,35 @@ ExpressionStatement: // Only membercalls to variables expression=PrefixExpression; -EXPR_OPERATOR returns string: - '^' | '*' | '/' | '+' | '-' | '<' | '>' | '=' | '<=' | '>=' | '<>' | 'AND' | 'OR'// from the BBx docu -; - Expression: BinaryExpression ({infer StringMask.left=current} ':' right=Expression)? //string mask ; + BinaryExpression infers Expression: - PrefixExpression ( - {infer BinaryExpression.left=current} operator=EXPR_OPERATOR right=(Expression| SymbolicLabelRef) - )? -; + RelationalExpr ({infer BinaryExpression.left=current} operator=('AND'|'OR') right=RelationalExpr)* + ; + +RelationalExpr infers Expression: + AdditiveExpr ({infer BinaryExpression.left=current} operator=('<' | '>' | '=' | '<=' | '>=' | '<>' ) right=AdditiveExpr)* + ; + +AdditiveExpr infers Expression: + MultiplicativeExpr ({infer BinaryExpression.left=current} operator=('+' | '-') right=MultiplicativeExpr)* + ; + +MultiplicativeExpr infers Expression: + ExponentiationExpr ({infer BinaryExpression.left=current} operator=('*' | '/') right=ExponentiationExpr)* + ; + +ExponentiationExpr infers Expression: + PrefixExpression ({infer BinaryExpression.left=current} operator='^' right=PrefixExpression)* + ; PrefixExpression infers Expression: {infer PrefixExpression} operator=('!'|'-'|'+') expression=MemberCall | MemberCall -; + ; MemberCall infers Expression: PrimaryExpression @@ -747,7 +758,9 @@ PrimaryExpression infers Expression: | Literal | Mnemonic | PositionalMnemonic - | ConstructorCall; + | ConstructorCall + | SymbolicLabelName + ; SymbolRef infers Expression: {infer SymbolRef} instanceAccess?='#'? symbol=[NamedElement:FeatureName] (isMethodCall?='(' (args+=Expression (',' args+=Expression)*)? Err? RPAREN)? @@ -808,7 +821,7 @@ EscapeId returns string: | 'TBL' | 'TIM' | 'BEGIN' | 'CLEAR' | 'ESCAPE' | 'DROP' | 'ENTER' | 'SETOPTS' | 'PRECISION' | 'RELEASE' | 'RENAME' | 'TO' | 'MKDIR' | 'RMDIR' | 'CHDIR' | 'EXECUTE' | 'KEY' | 'IND' | 'DIR' | 'ISZ' | 'DEF' | 'STOP' | 'RESET' | 'RETRY' | 'KNUM' | 'LEN' | 'SIZ' | 'BACKGROUND' | 'DELETE' | 'LOCK' | 'UNLOCK' | 'DIM' | 'SQLCOMMIT' | 'SQLROLLBACK' | 'SQLEXEC' | 'SQLCLOSE' | 'START' | 'DOM' | 'CHANOPT' - | 'FILEOPT' | 'REMOVE' | 'SAVE' | 'SORT' | 'FULLTEXT' | 'INDEXED' | 'RESTORE' | 'SETDAY' | 'SETTERM' | 'SETTIME' | 'INTERFACE' + | 'FILEOPT' | 'REMOVE' | 'SAVE' | 'SORT' | 'FULLTEXT' | 'INDEXED' | 'RESTORE' | 'SETDAY' | 'SETTERM' | 'SETTIME' | 'INTERFACE' | 'ENDIF' | 'FI' ; // BBx Library @@ -841,7 +854,7 @@ LibVariable returns LibVariable: LibSymbolicLabel returns LibSymbolicLabelDecl: (docu=DOCU)? - 'label' name=SYMBOLIC_LABEL_NAME + 'label' name=SymbolicLabelName ; LibEventType returns LibEventType: @@ -849,6 +862,8 @@ LibEventType returns LibEventType: 'eventType' name=ValidName ; +SymbolicLabelName returns string: '*' ValidName; + hidden terminal WS: /\s+/; terminal COMMENT: /([rR][eE][mM])([ \t][^\n\r]*)?[\n\r]+/; // (rEm)(space or tab)(all but linebreak)(linebreak) @@ -875,7 +890,6 @@ terminal HEX_STRING: /\$[0-9a-fA-F]*\$/; // $0A1E$, $$ = Null string terminal MNEMONIC: /'[0-9a-zA-Z_]*'/; // 'hide', 'lf', 'BOX'(10,12,4,4), 'FONT'("pica") terminal DOCU: /\/@@[\s\S]*?@\//; -terminal SYMBOLIC_LABEL_NAME: /\*[a-zA-Z]+/; // Types type DefFunctionStatement = DefReturn | Statement; diff --git a/bbj-vscode/test/parser.test.ts b/bbj-vscode/test/parser.test.ts index 086ae34..88a722b 100644 --- a/bbj-vscode/test/parser.test.ts +++ b/bbj-vscode/test/parser.test.ts @@ -1823,6 +1823,16 @@ describe('Parser Tests', () => { expectNoValidationErrors(result); }); + test('Multiply without spaces', async () => { + const result = await parse(` + let p_width = 100 + let p_hPadding = 20 + let imageW = int(p_width + (2*p_hPadding)) + `, { validation: true }); + expectNoParserLexerErrors(result); + expectNoValidationErrors(result); + }); + test('Switch-Default without semicolon', async () => { const result = await parse(` LET t$ = "hallo" diff --git a/bbj-vscode/test/validation.test.ts b/bbj-vscode/test/validation.test.ts index b3e74b8..dcc3b19 100644 --- a/bbj-vscode/test/validation.test.ts +++ b/bbj-vscode/test/validation.test.ts @@ -9,7 +9,7 @@ import { beforeAll, describe, expect, test } from 'vitest'; import { expectError, expectNoIssues, validationHelper } from 'langium/test'; import { createBBjServices } from '../src/language/bbj-module.js'; -import { Program, isBinaryExpression, isEraseStatement, isInitFileStatement, isKeyedFileStatement, isKeywordStatement, isMemberCall } from '../src/language/generated/ast.js'; +import { Program, isBinaryExpression, isEraseStatement, isInitFileStatement, isKeyedFileStatement, isKeywordStatement, isSymbolicLabelRef, isMemberCall } from '../src/language/generated/ast.js'; import { findByIndex, findFirst, initializeWorkspace } from './test-helper.js'; describe('BBj validation', async () => { @@ -384,4 +384,15 @@ describe('BBj validation', async () => { `); expectNoIssues(validationResult); }); + + test('SymbolicLabelRef text should not contain whitespace', async () => { + const validationResult = await validate(` + exitto * NEXT + `); + const symbolicLabelRef = findFirst(validationResult.document, isSymbolicLabelRef, true); + expect(symbolicLabelRef).toBeDefined(); + expectError(validationResult, 'Symbolic label reference may not contain whitespace.', { + node: symbolicLabelRef + }); + }); }); \ No newline at end of file diff --git a/examples/issue188-multiply.bbj b/examples/issue188-multiply.bbj new file mode 100644 index 0000000..cca03b3 --- /dev/null +++ b/examples/issue188-multiply.bbj @@ -0,0 +1,3 @@ +let p_width = 100 +let p_hPadding = 20 +let imageW = 2*p_hPadding \ No newline at end of file