From 1c9673a850e831b3aad0b6df9815f185b7519602 Mon Sep 17 00:00:00 2001 From: Manuel Laggner Date: Sun, 16 Nov 2025 08:50:58 +0100 Subject: [PATCH] Add modern ABAP 7.40+ and CDS keywords Updated the ABAP lexer to support more modern keywords and CDS functions/annotations --- lib/rouge/lexers/abap.rb | 207 ++++++++++++++++++++---------------- spec/lexers/abap_spec.rb | 219 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 336 insertions(+), 90 deletions(-) diff --git a/lib/rouge/lexers/abap.rb b/lib/rouge/lexers/abap.rb index 8be9e950ce..7dffc0a09d 100644 --- a/lib/rouge/lexers/abap.rb +++ b/lib/rouge/lexers/abap.rb @@ -2,6 +2,7 @@ # frozen_string_literal: true # ABAP elements taken from http://help.sap.com/abapdocu_750/en/index.htm?file=abapdo.htm +# Updated with modern ABAP 7.40+ and CDS keywords from ABAP 7.58 documentation module Rouge module Lexers @@ -17,62 +18,62 @@ def self.keywords *-INPUT ?TO ABAP-SOURCE ABBREVIATED ABS ABSTRACT ACCEPT ACCEPTING ACCORDING ACCP ACTIVATION ACTUAL ADD ADD-CORRESPONDING ADJACENT AFTER ALIAS ALIASES ALIGN ALL ALLOCATE ALPHA ANALYSIS ANALYZER AND - ANY APPEND APPENDAGE APPENDING APPLICATION ARCHIVE AREA ARITHMETIC - AS ASCENDING ASPECT ASSERT ASSIGN ASSIGNED ASSIGNING ASSOCIATION - ASYNCHRONOUS AT ATTRIBUTES AUTHORITY AUTHORITY-CHECK AVG BACK - BACKGROUND BACKUP BACKWARD BADI BASE BEFORE BEGIN BETWEEN BIG BINARY - BINTOHEX BIT BIT-AND BIT-NOT BIT-OR BIT-XOR BLACK BLANK BLANKS BLOB - BLOCK BLOCKS BLUE BOUND BOUNDARIES BOUNDS BOXED BREAK-POINT BT - BUFFER BY BYPASSING BYTE BYTE-CA BYTE-CN BYTE-CO BYTE-CS BYTE-NA - BYTE-NS BYTE-ORDER CA CALL CALLING CASE CAST CASTING CATCH CEIL - CENTER CENTERED CHAIN CHAIN-INPUT CHAIN-REQUEST CHANGE CHANGING - CHANNELS CHAR CHAR-TO-HEX CHARACTER CHECK CHECKBOX CIRCULAR CLASS - CLASS-CODING CLASS-DATA CLASS-EVENTS CLASS-METHODS CLASS-POOL - CLEANUP CLEAR CLIENT CLNT CLOB CLOCK CLOSE CN CO COALESCE CODE - CODING COLLECT COLOR COLUMN COLUMNS COL_BACKGROUND COL_GROUP - COL_HEADING COL_KEY COL_NEGATIVE COL_NORMAL COL_POSITIVE COL_TOTAL - COMMENT COMMENTS COMMIT COMMON COMMUNICATION COMPARING COMPONENT - COMPONENTS COMPRESSION COMPUTE CONCAT CONCATENATE CONCAT_WITH_SPACE - COND CONDENSE CONDITION CONNECT CONNECTION CONSTANTS CONTEXT - CONTEXTS CONTINUE CONTROL CONTROLS CONV CONVERSION CONVERT COPIES - COPY CORRESPONDING COUNT COUNTRY COVER CP CPI CREATE CREATING - CRITICAL CS CUKY CURR CURRENCY CURRENCY_CONVERSION CURRENT CURSOR - CURSOR-SELECTION CUSTOMER CUSTOMER-FUNCTION CX_DYNAMIC_CHECK - CX_NO_CHECK CX_ROOT CX_SQL_EXCEPTION CX_STATIC_CHECK DANGEROUS DATA - DATABASE DATAINFO DATASET DATE DATS DATS_ADD_DAYS DATS_ADD_MONTHS - DATS_DAYS_BETWEEN DATS_IS_VALID DAYLIGHT DD/MM/YY DD/MM/YYYY DDMMYY - DEALLOCATE DEC DECIMALS DECIMAL_SHIFT DECLARATIONS DEEP DEFAULT - DEFERRED DEFINE DEFINING DEFINITION DELETE DELETING DEMAND - DEPARTMENT DESCENDING DESCRIBE DESTINATION DETAIL DF16_DEC DF16_RAW - DF16_SCL DF34_DEC DF34_RAW DF34_SCL DIALOG DIRECTORY DISCONNECT - DISPLAY DISPLAY-MODE DISTANCE DISTINCT DIV DIVIDE - DIVIDE-CORRESPONDING DIVISION DO DUMMY DUPLICATE DUPLICATES DURATION - DURING DYNAMIC DYNPRO E EDIT EDITOR-CALL ELSE ELSEIF EMPTY ENABLED - ENABLING ENCODING END END-ENHANCEMENT-SECTION END-LINES - END-OF-DEFINITION END-OF-FILE END-OF-PAGE END-OF-SELECTION - END-TEST-INJECTION END-TEST-SEAM ENDAT ENDCASE ENDCATCH ENDCHAIN - ENDCLASS ENDDO ENDENHANCEMENT ENDEXEC ENDFORM ENDFUNCTION ENDIAN - ENDIF ENDING ENDINTERFACE ENDLOOP ENDMETHOD ENDMODULE ENDON - ENDPROVIDE ENDSELECT ENDTRY ENDWHILE ENDWITH ENGINEERING ENHANCEMENT - ENHANCEMENT-POINT ENHANCEMENT-SECTION ENHANCEMENTS ENTRIES ENTRY - ENVIRONMENT EQ EQUIV ERRORMESSAGE ERRORS ESCAPE ESCAPING EVENT - EVENTS EXACT EXCEPT EXCEPTION EXCEPTION-TABLE EXCEPTIONS EXCLUDE - EXCLUDING EXEC EXECUTE EXISTS EXIT EXIT-COMMAND EXPAND EXPANDING - EXPIRATION EXPLICIT EXPONENT EXPORT EXPORTING EXTEND EXTENDED - EXTENSION EXTRACT FAIL FETCH FIELD FIELD-GROUPS FIELD-SYMBOL - FIELD-SYMBOLS FIELDS FILE FILTER FILTER-TABLE FILTERS FINAL FIND - FIRST FIRST-LINE FIXED-POINT FKEQ FKGE FLOOR FLTP FLUSH FONT FOR - FORM FORMAT FORWARD FOUND FRAME FRAMES FREE FRIENDS FROM FUNCTION - FUNCTION-POOL FUNCTIONALITY FURTHER GAPS GE GENERATE GET - GET_PRINT_PARAMETERS GIVING GKEQ GKGE GLOBAL GRANT GREEN GROUP - GROUPS GT HANDLE HANDLER HARMLESS HASHED HAVING HDB HEAD-LINES - HEADER HEADERS HEADING HELP-ID HELP-REQUEST HEXTOBIN HIDE HIGH HINT - HOLD HOTSPOT I ICON ID IDENTIFICATION IDENTIFIER IDS IF - IF_ABAP_CLOSE_RESOURCE IF_ABAP_CODEPAGE IF_ABAP_DB_BLOB_HANDLE - IF_ABAP_DB_CLOB_HANDLE IF_ABAP_DB_LOB_HANDLE IF_ABAP_DB_READER - IF_ABAP_DB_WRITER IF_ABAP_READER IF_ABAP_WRITER IF_MESSAGE - IF_OS_CA_INSTANCE IF_OS_CA_PERSISTENCY IF_OS_FACTORY IF_OS_QUERY - IF_OS_QUERY_MANAGER IF_OS_QUERY_OPTIONS IF_OS_STATE + ANNOTATION ANNOTATIONS ANY APPEND APPENDAGE APPENDING APPLICATION + ARCHIVE AREA ARITHMETIC AS ASCENDING ASPECT ASSERT ASSIGN ASSIGNED + ASSIGNING ASSOCIATION ASSOCIATIONS ASYNCHRONOUS AT ATTRIBUTES + AUTHORITY AUTHORITY-CHECK AVG BACK BACKGROUND BACKUP BACKWARD BADI + BASE BEFORE BEGIN BETWEEN BIG BINARY BINTOHEX BIT BIT-AND BIT-NOT + BIT-OR BIT-XOR BLACK BLANK BLANKS BLOB BLOCK BLOCKS BLUE BOUND + BOUNDARIES BOUNDS BOXED BREAK-POINT BT BUFFER BY BYPASSING BYTE + BYTE-CA BYTE-CN BYTE-CO BYTE-CS BYTE-NA BYTE-NS BYTE-ORDER CA CALL + CALLING CASE CAST CASTING CATCH CEIL CENTER CENTERED CHAIN + CHAIN-INPUT CHAIN-REQUEST CHANGE CHANGING CHANNELS CHAR CHAR-TO-HEX + CHARACTER CHECK CHECKBOX CIRCULAR CLASS CLASS-CODING CLASS-DATA + CLASS-EVENTS CLASS-METHODS CLASS-POOL CLEANUP CLEAR CLIENT CLNT CLOB + CLOCK CLOSE CN CO COALESCE CODE CODING COLLECT COLOR COLUMN COLUMNS + COL_BACKGROUND COL_GROUP COL_HEADING COL_KEY COL_NEGATIVE COL_NORMAL + COL_POSITIVE COL_TOTAL COMMENT COMMENTS COMMIT COMMON COMMUNICATION + COMPARING COMPONENT COMPONENTS COMPOSITION COMPRESSION COMPUTE + CONCAT CONCATENATE CONCAT_WITH_SPACE COND CONDENSE CONDITION CONNECT + CONNECTION CONSTANTS CONTEXT CONTEXTS CONTINUE CONTROL CONTROLS CONV + CONVERSION CONVERT COPIES COPY CORRESPONDING COUNT COUNTRY COVER CP + CPI CREATE CREATING CRITICAL CS CUKY CURR CURRENCY + CURRENCY_CONVERSION CURRENT CURSOR CURSOR-SELECTION CUSTOMER + CUSTOMER-FUNCTION CX_DYNAMIC_CHECK CX_NO_CHECK CX_ROOT + CX_SQL_EXCEPTION CX_STATIC_CHECK DANGEROUS DATA DATABASE DATAINFO + DATASET DATE DATS DATS_ADD_DAYS DATS_ADD_MONTHS DATS_DAYS_BETWEEN + DATS_IS_VALID DAYLIGHT DD/MM/YY DD/MM/YYYY DDMMYY DEALLOCATE DEC + DECIMALS DECIMAL_SHIFT DECLARATIONS DEEP DEFAULT DEFERRED DEFINE + DEFINING DEFINITION DELETE DELETING DEMAND DEPARTMENT DESCENDING + DESCRIBE DESTINATION DETAIL DF16_DEC DF16_RAW DF16_SCL DF34_DEC + DF34_RAW DF34_SCL DIALOG DIRECTORY DISCONNECT DISPLAY DISPLAY-MODE + DISTANCE DISTINCT DIV DIVIDE DIVIDE-CORRESPONDING DIVISION DO DUMMY + DUPLICATE DUPLICATES DURATION DURING DYNAMIC DYNPRO E EDIT + EDITOR-CALL ELSE ELSEIF EMPTY ENABLED ENABLING ENCODING END + END-ENHANCEMENT-SECTION END-LINES END-OF-DEFINITION END-OF-FILE + END-OF-PAGE END-OF-SELECTION END-TEST-INJECTION END-TEST-SEAM ENDAT + ENDCASE ENDCATCH ENDCHAIN ENDCLASS ENDDO ENDENHANCEMENT ENDEXEC + ENDFORM ENDFUNCTION ENDIAN ENDIF ENDING ENDINTERFACE ENDLOOP + ENDMETHOD ENDMODULE ENDON ENDPROVIDE ENDSELECT ENDTRY ENDWHILE + ENDWITH ENGINEERING ENHANCEMENT ENHANCEMENT-POINT ENHANCEMENT-SECTION + ENHANCEMENTS ENTITIES ENTITY ENTRIES ENTRY ENUM ENVIRONMENT EQ EQUIV + ERRORMESSAGE ERRORS ESCAPE ESCAPING EVENT EVENTS EXACT EXCEPT + EXCEPTION EXCEPTION-TABLE EXCEPTIONS EXCLUDE EXCLUDING EXEC EXECUTE + EXISTS EXIT EXIT-COMMAND EXPAND EXPANDING EXPIRATION EXPLICIT + EXPONENT EXPORT EXPORTING EXTEND EXTENDED EXTENSION EXTRACT FAIL + FETCH FIELD FIELD-GROUPS FIELD-SYMBOL FIELD-SYMBOLS FIELDS FILE + FILTER FILTER-TABLE FILTERS FINAL FIND FIRST FIRST-LINE FIXED-POINT + FKEQ FKGE FLOOR FLTP FLUSH FONT FOR FORM FORMAT FORWARD FOUND FRAME + FRAMES FREE FRIENDS FROM FUNCTION FUNCTION-POOL FUNCTIONALITY + FURTHER GAPS GE GENERATE GET GET_PRINT_PARAMETERS GIVING GKEQ GKGE + GLOBAL GRANT GREEN GROUP GROUPS GT HANDLE HANDLER HARMLESS HASHED + HAVING HDB HEAD-LINES HEADER HEADERS HEADING HELP-ID HELP-REQUEST + HEXTOBIN HIDE HIGH HINT HOLD HOTSPOT I ICON ID IDENTIFICATION + IDENTIFIER IDS IF IF_ABAP_CLOSE_RESOURCE IF_ABAP_CODEPAGE + IF_ABAP_DB_BLOB_HANDLE IF_ABAP_DB_CLOB_HANDLE IF_ABAP_DB_LOB_HANDLE + IF_ABAP_DB_READER IF_ABAP_DB_WRITER IF_ABAP_READER IF_ABAP_WRITER + IF_MESSAGE IF_OS_CA_INSTANCE IF_OS_CA_PERSISTENCY IF_OS_FACTORY + IF_OS_QUERY IF_OS_QUERY_MANAGER IF_OS_QUERY_OPTIONS IF_OS_STATE IF_OS_TRANSACTION IF_OS_TRANSACTION_MANAGER IF_SERIALIZABLE_OBJECT IF_SHM_BUILD_INSTANCE IF_SYSTEM_UUID IF_T100_DYN_MSG IF_T100_MESSAGE IGNORE IGNORING IMMEDIATELY IMPLEMENTATION IMPLEMENTATIONS @@ -87,7 +88,7 @@ def self.keywords LET LEVEL LEVELS LIKE LINE LINE-COUNT LINE-SELECTION LINE-SIZE LINEFEED LINES LIST LIST-PROCESSING LISTBOX LITTLE LLANG LOAD LOAD-OF-PROGRAM LOB LOCAL LOCALE LOCATOR LOG-POINT LOGFILE LOGICAL - LONG LOOP LOW LOWER LPAD LPI LRAW LT LTRIM M MAIL MAIN MAJOR-ID + LONG LOOP LOW LOWER LPAD LPI LRAW LT LTRIM M MAIL MAIN MAJOR-ID MANY MAPPING MARGIN MARK MASK MATCH MATCHCODE MAX MAXIMUM MEDIUM MEMBERS MEMORY MESH MESSAGE MESSAGE-ID MESSAGES MESSAGING METHOD METHODS MIN MINIMUM MINOR-ID MM/DD/YY MM/DD/YYYY MMDDYY MOD MODE MODIF MODIFIER @@ -97,23 +98,23 @@ def self.keywords NO-GAP NO-GAPS NO-GROUPING NO-HEADING NO-SCROLLING NO-SIGN NO-TITLE NO-TOPOFPAGE NO-ZERO NODE NODES NON-UNICODE NON-UNIQUE NOT NP NS NULL NUMBER NUMC O OBJECT OBJECTS OBLIGATORY OCCURRENCE OCCURRENCES - OCCURS OF OFF OFFSET ON ONLY OPEN OPTION OPTIONAL OPTIONS OR ORDER - OTHER OTHERS OUT OUTER OUTPUT OUTPUT-LENGTH OVERFLOW OVERLAY PACK - PACKAGE PAD PADDING PAGE PAGES PARAMETER PARAMETER-TABLE PARAMETERS - PART PARTIALLY PATTERN PERCENTAGE PERFORM PERFORMING PERSON PF - PF-STATUS PINK PLACES POOL POSITION POS_HIGH POS_LOW PRAGMAS PREC - PRECOMPILED PREFERRED PRESERVING PRIMARY PRINT PRINT-CONTROL - PRIORITY PRIVATE PROCEDURE PROCESS PROGRAM PROPERTY PROTECTED - PROVIDE PUBLIC PUSH PUSHBUTTON PUT QUAN QUEUE-ONLY QUICKINFO - RADIOBUTTON RAISE RAISING RANGE RANGES RAW RAWSTRING READ READ-ONLY - READER RECEIVE RECEIVED RECEIVER RECEIVING RED REDEFINITION REDUCE - REDUCED REF REFERENCE REFRESH REGEX REJECT REMOTE RENAMING REPLACE - REPLACEMENT REPLACING REPORT REQUEST REQUESTED RESERVE RESET + OCCURS OF OFF OFFSET ON ONE ONLY OPEN OPTION OPTIONAL OPTIONS OR + ORDER OTHER OTHERS OUT OUTER OUTPUT OUTPUT-LENGTH OVERFLOW OVERLAY + PACK PACKAGE PAD PADDING PAGE PAGES PARAMETER PARAMETER-TABLE + PARAMETERS PART PARTIALLY PATTERN PERCENTAGE PERFORM PERFORMING + PERSON PF PF-STATUS PINK PLACES POOL POSITION POS_HIGH POS_LOW + PRAGMAS PREC PRECOMPILED PREFERRED PRESERVING PRIMARY PRINT + PRINT-CONTROL PRIORITY PRIVATE PROCEDURE PROCESS PROGRAM PROPERTY + PROTECTED PROVIDE PUBLIC PUSH PUSHBUTTON PUT QUAN QUEUE-ONLY + QUICKINFO RADIOBUTTON RAISE RAISING RANGE RANGES RAW RAWSTRING READ + READ-ONLY READER RECEIVE RECEIVED RECEIVER RECEIVING RED REDEFINITION + REDUCE REDUCED REF REFERENCE REFRESH REGEX REJECT REMOTE RENAMING + REPLACE REPLACEMENT REPLACING REPORT REQUEST REQUESTED RESERVE RESET RESOLUTION RESPECTING RESPONSIBLE RESULT RESULTS RESUMABLE RESUME RETRY RETURN RETURNCODE RETURNING RETURNS RIGHT RIGHT-JUSTIFIED RIGHTPLUS RIGHTSPACE RISK RMC_COMMUNICATION_FAILURE RMC_INVALID_STATUS RMC_SYSTEM_FAILURE ROLE ROLLBACK ROUND ROWS RPAD - RTRIM RUN SAP SAP-SPOOL SAVING SCALE_PRESERVING + RTRIM RUN SAP SAP-SPOOL SAVING SCALAR SCALE_PRESERVING SCALE_PRESERVING_SCIENTIFIC SCAN SCIENTIFIC SCIENTIFIC_WITH_LEADING_ZERO SCREEN SCROLL SCROLL-BOUNDARY SCROLLING SEARCH SECONDARY SECONDS SECTION SELECT SELECT-OPTIONS SELECTION @@ -123,8 +124,8 @@ def self.keywords SMART SOME SORT SORTABLE SORTED SOURCE SPACE SPECIFIED SPLIT SPOOL SPOTS SQL SQLSCRIPT SSTRING STABLE STAMP STANDARD START-OF-SELECTION STARTING STATE STATEMENT STATEMENTS STATIC STATICS STATUSINFO - STEP-LOOP STOP STRING STRUCTURE STRUCTURES STYLE SUBKEY SUBMATCHES - SUBMIT SUBROUTINE SUBSCREEN SUBSTRING SUBTRACT + STEP-LOOP STEP STOP STRING STRUCTURE STRUCTURES STYLE SUBKEY + SUBMATCHES SUBMIT SUBROUTINE SUBSCREEN SUBSTRING SUBTRACT SUBTRACT-CORRESPONDING SUFFIX SUM SUMMARY SUMMING SUPPLIED SUPPLY SUPPRESS SWITCH SWITCHSTATES SYMBOL SYNCPOINTS SYNTAX SYNTAX-CHECK SYNTAX-TRACE SYST SYSTEM-CALL SYSTEM-EXCEPTIONS SYSTEM-EXIT TAB @@ -147,23 +148,26 @@ def self.keywords def self.builtins @keywords = Set.new %w( - acos apply asin assign atan attribute bit-set boolc boolx call - call-method cast ceil cfunc charlen char_off class_constructor clear - cluster cmax cmin cnt communication_failure concat_lines_of cond - cond-var condense constructor contains contains_any_not_of - contains_any_of copy cos cosh count count_any_not_of count_any_of - create cursor data dbmaxlen dbtab deserialize destructor distance - empty error_message escape exp extensible find find_any_not_of - find_any_of find_end floor frac from_mixed group hashed header idx - include index insert ipow itab key lax lines line_exists line_index - log log10 loop loop_key match matches me mesh_path namespace nmax - nmin node numeric numofchar object parameter primary_key read ref - repeat replace rescale resource_failure reverse root round segment - sender serialize shift_left shift_right sign simple sin sinh skip - sorted space sqrt standard strlen substring substring_after - substring_before substring_from substring_to sum switch switch-var - system_failure table table_line tan tanh template text to_lower - to_mixed to_upper transform translate trunc type value variable write + abs acos add_days add_months add_seconds apply asin assign atan + attribute bit-set boolc boolx call call-method cast ceil cfunc + charlen char_off class_constructor clear cluster cmax cmin cnt + coalesce communication_failure concat concat_lines_of cond cond-var + condense constructor contains contains_any_not_of contains_any_of + conv copy corresponding cos cosh count count_any_not_of count_any_of + create currency_conversion current_utctimestamp cursor data + days_between dbmaxlen dbtab deserialize destructor distance div + division empty error_message escape exact exp extensible filter find + find_any_not_of find_any_of find_end floor frac from_mixed group + hashed header idx include index insert ipow is_valid itab key lax let + lines line_exists line_index log log10 loop loop_key match matches me + mesh_path mod namespace new nmax nmin node numeric numofchar object + parameter primary_key read reduce ref repeat replace rescale + resource_failure reverse root round seconds_between segment sender + serialize shift_left shift_right sign simple sin sinh skip sorted + space sqrt standard strlen substring substring_after substring_before + substring_from substring_to sum switch switch-var system_failure + table table_line tan tanh template text to_lower to_mixed to_upper + transform translate trunc type unit_conversion value variable write xsdbool xsequence xstrlen ) end @@ -187,8 +191,12 @@ def self.new_keywords rule %r/".*/, Comment::Single rule %r(^\*.*), Comment::Multiline rule %r/\d+/, Num::Integer + + # String templates |...| + rule %r/\|/, Str::Interpol, :string_template + rule %r/('|`)/, Str::Single, :single_string - rule %r/[\[\]\(\)\{\}\.,:\|]/, Punctuation + rule %r/[\[\]\(\)\{\}\.,:]/, Punctuation # builtins / new ABAP 7.40 keywords (@DATA(), ...) rule %r/(->|=>)?([A-Za-z][A-Za-z0-9_\-]*)(\()/ do |m| @@ -206,6 +214,19 @@ def self.new_keywords token Punctuation, m[3] end + # hyphenated keywords (like NON-UNIQUE) + rule %r/[A-Za-z][A-Za-z0-9_]*(-[A-Za-z][A-Za-z0-9_]*)+/ do |m| + if self.class.keywords.include? m[0].upcase + token Keyword + else + token Name + end + end + + # structure component access (variable-component should not be highlighted as keyword) + # this rule matches: word-word where the second part is lowercase or starts lowercase + rule %r/[A-Za-z][A-Za-z0-9_]*-[a-z][A-Za-z0-9_]*/, Name + # keywords, types and normal text rule %r/\w\w*/ do |m| if self.class.keywords.include? m[0].upcase @@ -235,6 +256,12 @@ def self.new_keywords rule %r/[^\\'`]+/, Str::Single end + state :string_template do + rule %r/\{[^}]*\}/, Str::Interpol # embedded expressions + rule %r/\|/, Str::Interpol, :pop! + rule %r/[^|{]+/, Str::Interpol + end + end end end diff --git a/spec/lexers/abap_spec.rb b/spec/lexers/abap_spec.rb index 3c3d382f89..cf0cf5a7d9 100644 --- a/spec/lexers/abap_spec.rb +++ b/spec/lexers/abap_spec.rb @@ -15,4 +15,223 @@ assert_guess :mimetype => 'text/x-abap' end end + + describe 'lexing' do + include Support::Lexing + + it 'recognizes modern constructor operators' do + # NEW operator + assert_tokens_equal 'DATA(obj) = NEW class( )', + ['Keyword', 'DATA'], + ['Punctuation', '('], + ['Name', 'obj'], + ['Punctuation', ')'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Keyword', 'NEW'], + ['Text', ' '], + ['Name', 'class'], + ['Punctuation', '('], + ['Text', ' '], + ['Punctuation', ')'] + + # VALUE operator + assert_tokens_equal 'itab = VALUE #( )', + ['Name', 'itab'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Keyword', 'VALUE'], + ['Text', ' '], + ['Operator', '#'], + ['Punctuation', '('], + ['Text', ' '], + ['Punctuation', ')'] + + # CORRESPONDING operator + assert_tokens_equal 'struct2 = CORRESPONDING #( struct1 )', + ['Name', 'struct2'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Keyword', 'CORRESPONDING'], + ['Text', ' '], + ['Operator', '#'], + ['Punctuation', '('], + ['Text', ' '], + ['Name', 'struct1'], + ['Text', ' '], + ['Punctuation', ')'] + end + + it 'recognizes CDS keywords' do + # DEFINE VIEW ENTITY + assert_tokens_equal 'DEFINE VIEW ENTITY ZTest', + ['Keyword', 'DEFINE'], + ['Text', ' '], + ['Keyword', 'VIEW'], + ['Text', ' '], + ['Keyword', 'ENTITY'], + ['Text', ' '], + ['Name', 'ZTest'] + + # ASSOCIATION + assert_tokens_equal 'ASSOCIATION TO ZEntity', + ['Keyword', 'ASSOCIATION'], + ['Text', ' '], + ['Keyword', 'TO'], + ['Text', ' '], + ['Name', 'ZEntity'] + + # COMPOSITION + assert_tokens_equal 'COMPOSITION OF ZChild', + ['Keyword', 'COMPOSITION'], + ['Text', ' '], + ['Keyword', 'OF'], + ['Text', ' '], + ['Name', 'ZChild'] + end + + it 'recognizes CDS and modern ABAP functions as builtins' do + # Modern constructor functions + assert_tokens_equal 'lv_result = conv( lv_value )', + ['Name', 'lv_result'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Name.Builtin', 'conv'], + ['Punctuation', '('], + ['Text', ' '], + ['Name', 'lv_value'], + ['Text', ' '], + ['Punctuation', ')'] + + # CDS numeric functions - note 'b' is a type keyword, use different variable name + assert_tokens_equal 'div( arg1 arg2 )', + ['Name.Builtin', 'div'], + ['Punctuation', '('], + ['Text', ' '], + ['Name', 'arg1'], + ['Text', ' '], + ['Name', 'arg2'], + ['Text', ' '], + ['Punctuation', ')'] + + # CDS currency conversion + assert_tokens_equal 'currency_conversion( )', + ['Name.Builtin', 'currency_conversion'], + ['Punctuation', '('], + ['Text', ' '], + ['Punctuation', ')'] + + # REDUCE operator + assert_tokens_equal 'reduce( )', + ['Name.Builtin', 'reduce'], + ['Punctuation', '('], + ['Text', ' '], + ['Punctuation', ')'] + + # FILTER operator + assert_tokens_equal 'filter( )', + ['Name.Builtin', 'filter'], + ['Punctuation', '('], + ['Text', ' '], + ['Punctuation', ')'] + + # CORRESPONDING function + assert_tokens_equal 'corresponding( )', + ['Name.Builtin', 'corresponding'], + ['Punctuation', '('], + ['Text', ' '], + ['Punctuation', ')'] + end + + it 'recognizes COND and SWITCH expressions' do + # COND expression - note 'x' is a type keyword in ABAP + assert_tokens_equal 'lv_result = COND #( WHEN lv_var = 1 THEN a )', + ['Name', 'lv_result'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Keyword', 'COND'], + ['Text', ' '], + ['Operator', '#'], + ['Punctuation', '('], + ['Text', ' '], + ['Keyword', 'WHEN'], + ['Text', ' '], + ['Name', 'lv_var'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Literal.Number.Integer', '1'], + ['Text', ' '], + ['Keyword', 'THEN'], + ['Text', ' '], + ['Name', 'a'], + ['Text', ' '], + ['Punctuation', ')'] + + # SWITCH expression + assert_tokens_equal 'lv_result = SWITCH #( lv_var )', + ['Name', 'lv_result'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Keyword', 'SWITCH'], + ['Text', ' '], + ['Operator', '#'], + ['Punctuation', '('], + ['Text', ' '], + ['Name', 'lv_var'], + ['Text', ' '], + ['Punctuation', ')'] + end + + it 'recognizes LET expressions' do + assert_tokens_equal 'LET lv_var = 1 IN y', + ['Keyword', 'LET'], + ['Text', ' '], + ['Name', 'lv_var'], + ['Text', ' '], + ['Operator', '='], + ['Text', ' '], + ['Literal.Number.Integer', '1'], + ['Text', ' '], + ['Keyword', 'IN'], + ['Text', ' '], + ['Name', 'y'] + end + + it 'handles LOOP with STEP' do + assert_tokens_equal 'LOOP AT itab STEP 2', + ['Keyword', 'LOOP'], + ['Text', ' '], + ['Keyword', 'AT'], + ['Text', ' '], + ['Name', 'itab'], + ['Text', ' '], + ['Keyword', 'STEP'], + ['Text', ' '], + ['Literal.Number.Integer', '2'] + end + + it 'recognizes ANNOTATION keyword' do + assert_tokens_equal '@ANNOTATION.label', + ['Operator', '@'], + ['Keyword', 'ANNOTATION'], + ['Punctuation', '.'], + ['Name', 'label'] + end + + it 'recognizes cardinality keywords' do + assert_tokens_equal 'MANY TO ONE', + ['Keyword', 'MANY'], + ['Text', ' '], + ['Keyword', 'TO'], + ['Text', ' '], + ['Keyword', 'ONE'] + end + end end