/* REXX exec/edit macro to convert REXX to 'HILITE'd HTML */ /*** trace ?r ***************************************************** \| * * (C) Copyright Robert AH Prins, 2007-2016 * * (C) Stefan B, 2007-2007 * ************************************************************************ * ------------------------------------------------------------------ * * | Date | By | Remarks | * * |------------+------+----------------------------------------------| * * | | | | * * |------------+------+----------------------------------------------| * * | 2016-11-24 | RAHP | Add "ehixmit" interface | * * |------------+------+----------------------------------------------| * * | 2016-11-18 | RAHP | Multiple updates/simplifications | * * |------------+------+----------------------------------------------| * * | 2012-06-25 | RAHP | Add IP address | * * |------------+------+----------------------------------------------| * * | 2012-06-08 | RAHP | Add ¢ HTML entity | * * |------------+------+----------------------------------------------| * * | 2009-07-27 | RAHP | Use pop-up on ISPF to display progress | * * |------------+------+----------------------------------------------| * * | 2009-07-02 | RAHP | Add selection for short CSS 'em' colors | * * |------------+------+----------------------------------------------| * * | 2009-04-22 | RAHP | Update comment | * * |------------+------+----------------------------------------------| * * | 2009-04-16 | RAHP | Main parsing loop needs to end on exactly | * * | | | '==' equal, as '0d0a'x equals '' on Regina | * * |------------+------+----------------------------------------------| * * | 2009-04-01 | RAHP | Add font selection for generated HTML | * * |------------+------+----------------------------------------------| * * | 2009-02-09 | RAHP | RACF problem with 'html' extension @ NVSM | * * |------------+------+----------------------------------------------| * * | 2007-09-24 | RAHP | Further tweaks | * * |------------+------+----------------------------------------------| * * | 2007-09-18 | RAHP | - add GPL V3 License | * * | | | - use EHISUPP 'get_options' to retrieve some | * * | | | processing options | * * |------------+------+----------------------------------------------| * * | 2007-09-06 | RAHP | Cater for PC environment | * * |------------+------+----------------------------------------------| * * | 2007-08-30 | RAHP | Further fine-tuning (HTLM 4.01 Strict) | * * |------------+------+----------------------------------------------| * * | 2007-08-23 | RAHP | Factor out common 'EHIxxxx' routines | * * |------------+------+----------------------------------------------| * * | 2007-08-20 | RAHP | - remove more bugs (rc/sigl/source) | * * | | | - add (nested) parentheses hilighting | * * |------------+------+----------------------------------------------| * * | 2007-08-16 | RAHP | Reformat to Prins' standards, remove nested | * * | | | comment bug | * * |------------+------+----------------------------------------------| * * | 2007-08-08 | SB | Initial version | * * |------------+------+----------------------------------------------| * ************************************************************************ * EHIREXX is a REXX exec/edit macro that analyses REXX code and builds * * a HTML file with the color attributes as used by ISPF Edit. * * * * This file can be transferred to the PC by using ISPF Workstation * * Agent. In addition the exec might invoke the Windows application * * associated with file extension ".html" * * * * The exec runs as ISPF edit macro or might be used as line command * * on the extended member list of ISPF List Utility (usually menu * * option 3.4). * * * * In addition the exec can be invoked on the command line. In this * * case the dataset name has to be supplied as invocation parameter. * * * * Original author: Stefan B. (See <http://www.schlabb.de>), based on * * an idea of W. Schäfer, rubin Software, see * * <http://www.rubin-software.de/index.php> * * * * See <http://mvshelp.net/vbforums/showthread.php?t=24126> for the * * original question on how to parse nested comments in REXX. The parse * * code is an adaptation of Mark Baxter's reply. * * * * t_rex will contain the environment. It can be: * * * * - TSO - TSO/ISPF * * - MVS - z/OS (PGM=IRXJCL) * * - SYSTEM - Regina * * - COMMAND - PC DOS 7/2000 * * - CMD - Object REXX (OS/2, Windoze) * ************************************************************************ * Send questions, suggestions and/or bug reports to: * * * * robert@prino.org / robert.ah.prins@gmail.com * * * * Robert AH Prins * * Taboralaan 46 * * 8400 Oostende * * Belgium * ************************************************************************ * This program is free software: you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation, either version 3 of * * the License, or (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see <http://www.gnu.org/licenses/> * ***********************************************************************/ parse source source parse value source with . . moi . . . cmdenv aspace . t_rex = address() if t_rex = 'TSO' &, aspace = 'ISPF' then do "ispexec vget (zenvir)" envir = strip(substr(zenvir, 17, 8)) end else envir = 'OTHER' parse arg idsn call get_source /* Read the REXX program source */ call init_vars /* Initialize the global variables */ call build_html /* Now go on and build the HTML output */ call ehisupp 'generate_output,'sep','htmlout || sep ||, odsn || sep ||, title || sep ||, header || sep ||, footer || sep ||, htmlfont "ispexec vget (ehixmit) shared" if rc = 0 then do "ispexec verase (rexxparm) shared" "isredit can" exit end if t_rex = 'TSO' &, aspace = 'ISPF' then if envir \= 'BATCH' then do /***************************************************************** * Show the resulting dataset, if desired * *****************************************************************/ if view_html = 'YES' then "ispexec view dataset("odsn")" /***************************************************************** * Transfer the html file to the PC * *****************************************************************/ if xfer_wsa = 'YES' then call ehisupp 'xfer_and_show_html,'sep','dir_pc || sep ||, htmlfile || sep ||, odsn || sep ||, ipaddr || sep ||, start_browser end else do if macmode then "isredit end" end exit /*********************************************************************** * TRANSLATE_ENTITIES * * * * This procedure translates special characters to HTML entities * ***********************************************************************/ translate_entities: procedure expose special_chars special_html parse arg in out = '' if translate(in, ' ', special_chars) = in then out = in else do while in \== '' c = left(in, 1) k = wordpos(c, special_chars) if k \= 0 then out = out || word(special_html, k) else out = out || c in = substr(in, 2) end return out /*********************************************************************** * GET_SOURCE: * * * * Read the REXX source * ***********************************************************************/ get_source: macmode = 0 rxdata. = '' rxdata = '' pgm = '' select when t_rex = 'TSO' then call get_source_tso when t_rex = 'MVS' then call get_source_mvs otherwise call get_source_pc end return /*********************************************************************** * GET_SOURCE_TSO: * * * * Read the text when running under TSO * ***********************************************************************/ get_source_tso: if aspace = 'ISPF' then "isredit macro (parm) NOPROCESS" else rc = 4 /********************************************************************* * Running as edit macro * *********************************************************************/ if rc = 0 then do macmode = 1 if parm = '?' then do "isredit ehihelp" moi exit end "isredit process range HI" if rc <= 4 then do "isredit (ZF) = linenum .zfrange" "isredit (ZL) = linenum .zlrange" end else do "isredit ehihelp" moi exit end "isredit (DSN) = dataset" "isredit (MEM) = member" if mem = '' then idsn = "'" || dsn || "'" else do pgm = mem idsn = "'" || dsn || '(' || mem || ")'" end /***************************************************************** * Concatenate the full source, but indicate original line breaks * *****************************************************************/ i = 0 do j = +zf to +zl "isredit (DATALINE) = line" j i = i + 1 rxdata.i = strip(dataline, 'T') || ' ' || '0d0a'x end rxdata.0 = i olines = rxdata.0 end /********************************************************************* * Running as TSO command * *********************************************************************/ else do if idsn = '' then do msg = left('Error - No dataset name passed', 75) msg = msg left(moi 'can be used as an edit macro or', 'as a line command on the ISPF', 75) msg = msg left('dataset list utility. In both cases the', 'dataset name will be automatically', 75) msg = msg left('determined.', 75) msg = msg left('If you call' moi 'on the command line you', 'have to pass the name of the', 75) msg = msg left('dataset to be processed, e.g.', 75) msg = msg left('Command ===>' moi, '''my.rexx.dataset(test)''', 75) zedsmsg = '' zedlmsg = msg if t_rex = 'TSO' &, aspace = 'ISPF' &, envir \= 'BATCH' then "ispexec setmsg msg(ISRZ001)" else do while msg \= '' say left(msg, 75) msg = substr(msg, 76) end exit 8 end /***************************************************************** * Force single quotes around dataset name and check if it's OK * *****************************************************************/ idsn = "'" || strip(idsn,, '''') || "'" if sysdsn(idsn) \= 'OK' then do say 'Error - Dataset' idsn 'could not be found' exit 8 end /***************************************************************** * Extract member name, if present * *****************************************************************/ parse var idsn . '(' mem ')' if mem \= '' then pgm = mem /***************************************************************** * Read the code * *****************************************************************/ dynlib = 'dyn'random(99999) "alloc f("dynlib") da("idsn") shr reu" if rc > 0 then do say 'Error - Dataset' idsn 'could not be allocated - rc' rc exit 8 end "execio * diskr" dynlib "(finis)" if rc > 0 then do say 'Error - Dataset' idsn 'could not be read - rc' rc exit 8 end "free f("dynlib")" /***************************************************************** * Merge full source, indicating linebreaks * *****************************************************************/ olines = queued() i = 0 do queued() parse pull dataline if length(dataline) = 80 &, datatype(right(dataline, 8)) = 'NUM' then dataline = substr(dataline, 1, 72) i = i + 1 rxdata.i = strip(dataline, 'T') || ' ' || '0d0a'x end end return /*********************************************************************** * GET_SOURCE_PC: * * * * Read the text when running on the PC * ***********************************************************************/ get_source_pc: if idsn = '' then do say 'Syntax:' moi 'file.rex' exit 8 end do i = 1 by 1 while lines(idsn) dataline = linein(idsn) if length(dataline) = 80 &, datatype(right(dataline, 8)) = 'NUM' then dataline = substr(dataline, 1, 72) rxdata.i = strip(dataline, 'T') || ' ' || '0d0a'x end rxdata.0 = i - 1 olines = rxdata.0 return /*********************************************************************** * INIT_VARS: * * * * This procedure initialises the global variables * ***********************************************************************/ init_vars: /********************************************************************* * Parameter separator for EHISUPP exec * *********************************************************************/ sep = x2c(00)d2c(random(2**16))x2c(ff)d2c(random(2**16))x2c(00) sep = translate(sep, x2c(bababababa), ' <>&"') /********************************************************************* * Get processing options * *********************************************************************/ opt = ehisupp('get_options,'sep','moi) parse value opt with view_html (sep), xfer_wsa (sep), start_browser (sep), ispf_edit (sep), show_progress (sep), dir_pc (sep), htmlfont (sep), ipaddr (sep) . /********************************************************************* * Temporary output dataset * *********************************************************************/ if mem \= '' then odsn = "'" || userid() || '.' || mem || ".exec.html'" else odsn = "'" || userid() || '.' || moi || ".exec.html'" /********************************************************************* * Text strings for title, header and footer * *********************************************************************/ title = 'REXX exec:' strip(idsn,, '''') header = 'REXX exec:' strip(idsn,, '''') now = date('S') now = left(now, 4)'-'substr(now, 5, 2)'-'right(now, 2)'T'time() footer = 'Generated on' now 'by' userid() 'with' moi /********************************************************************* * Name of generated html file on PC * *********************************************************************/ if pgm \= '' then htmlfile = pgm || '.html' else htmlfile = 'rexxpgm.html' /********************************************************************* * HTML colors * * * * - lime(green) - default * * - red - keywords * * - white - quoted strings * * - aqua(turq) - comments * * - yellow - special characters * *********************************************************************/ col_dft = '<em class="l">' /* lime */ col_key = '<em class="r">' /* red */ col_str = '<em class="w">' /* white */ col_com = '<em class="t">' /* turq */ col_spc = '<em class="y">' /* yellow */ /********************************************************************* * Colors for nested parentheses * *********************************************************************/ col_par.0 = '<em class="f">' /* fuchsia */ col_par.1 = '<em class="y">' /* yellow */ col_par.2 = '<em class="w">' /* white */ col_par.3 = '<em class="r">' /* red */ col_par.4 = '<em class="t">' /* turq */ /********************************************************************* * HTML special characters and their defined entities * *********************************************************************/ special_chars = 'ä ö ü Ä Ö Ü ß < > & " ¢' special_html = 'ä ö ü Ä Ö Ü ß', '< > & " ¢' /********************************************************************* * Characters to be highlighted * *********************************************************************/ special_hilite = '+-*/=<>&^|:%\' /********************************************************************* * Characters separating words * *********************************************************************/ separator = " (=*-+/^%)&|<>'" || '"' return /*********************************************************************** * BUILD_HTML: * * * * This procedure builds the HTML output * ***********************************************************************/ build_html: /********************************************************************* * Load the list of REXX keywords, builtins, etc * *********************************************************************/ call build_list_of_keywords /********************************************************************* * Switches * *********************************************************************/ kwbegin = 1 /* Do we expect a new keyword ? */ in_com = 0 /* Inside a comment */ in_apost = 0 /* Inside a '(apost) delimited string */ in_quote = 0 /* Inside a "(quote) delimited string */ level = 0 /* Nested comment levels */ paren = 1 /* Nested parentheses level */ /********************************************************************* * Initialize the html output string * *********************************************************************/ if ispf_edit = 'ISPF' then htmlout = x2c(ff)ispf_edit || x2c(ff)right(olines, 6, '0')x2c(ff) else htmlout = '' tempout = '' /********************************************************************* * Loop over the code * *********************************************************************/ lip = time('E') lip = 1 r = 1 rxdata = rxdata.r do until rxdata == '' /******************************************************************* * Display (optional) progress messages * *******************************************************************/ if show_progress > 0 then if lip > 0 &, lip // show_progress = 0 then do progress = 'Elapsed time' right(time('E'), 12), '- lines processed' right(lip, 6) if t_rex = 'TSO' &, aspace = 'ISPF' then rc = ehisupp('monitor,'moi 'Progress,'progress) else say progress lip = -lip end c1 = left(rxdata, 1) c2 = left(rxdata, 2) /******************************************************************* * Is it a special character? * *******************************************************************/ sc = wordpos(c1, special_chars) /******************************************************************* * Is it a special hilite character? * *******************************************************************/ sh = pos(c1, special_hilite) kw = '' /* Initialize keyword */ /******************************************************************* * If we are at the beginning of a keyword ... * *******************************************************************/ if kwbegin = 1 &, pos(c1, separator) = 0 then do parse upper var rxdata kw /*************************************************************** * ... we search the next separator ... * ***************************************************************/ sep_pos = verify(kw, separator, 'M') /*************************************************************** * ... and save the keyword if separator is found * ***************************************************************/ if sep_pos > 0 then kw = left(kw, sep_pos - 1) kwbegin = 0 end /******************************************************************* * If we are on a separator we keep in mind that it's the beginning * * of a new keyword * *******************************************************************/ if pos(c1, separator) > 0 then kwbegin = 1 /******************************************************************* * Determine the HTML attributes for the data * *******************************************************************/ select /***************************************************************** * Spaces are kept unchanged - process multiple spaces in one go| * *****************************************************************/ when c1 == ' ' then do n = verify(rxdata, ' ') tempout = tempout || left(rxdata, n - 1) rxdata = substr(rxdata, n) end /***************************************************************** * Line break * *****************************************************************/ when c2 = '0d0a'x then do lip = abs(lip) + 1 tempout = tempout || '<br>' kwbegin = 1 rxdata = substr(rxdata, 3) end /***************************************************************** * End of single quoted string * *****************************************************************/ when in_apost &, c1 = "'" then do in_apost = 0 tempout = tempout || "'</em>" rxdata = substr(rxdata, 2) end /***************************************************************** * End of double quoted string * *****************************************************************/ when in_quote &, c1 = '"' then do in_quote = 0 tempout = tempout || '"</em>' rxdata = substr(rxdata, 2) end /***************************************************************** * Start of single quoted string * *****************************************************************/ when c1 = "'" &, \in_quote &, \in_com then do in_apost = 1 tempout = tempout || col_str"'" rxdata = substr(rxdata, 2) end /***************************************************************** * Start of double quoted string * *****************************************************************/ when c1 = '"' &, \in_apost &, \in_com then do in_quote = 1 tempout = tempout || col_str'"' rxdata = substr(rxdata, 2) end /***************************************************************** * Start of a nested comment * *****************************************************************/ when c2 = '/*' &, in_com then do level = level + 1 tempout = tempout || '/*' rxdata = substr(rxdata, 3) end /***************************************************************** * Start of a comment * *****************************************************************/ when c2 = '/*' &, \in_apost &, \in_quote then do in_com = 1 tempout = tempout || col_com'/*' rxdata = substr(rxdata, 3) end /***************************************************************** * End of a comment * *****************************************************************/ when c2 = '*/' &, level = 0 &, \in_apost &, \in_quote then do in_com = 0 tempout = tempout || '*/</em>' rxdata = substr(rxdata, 3) end /***************************************************************** * End of a nested comment * *****************************************************************/ when c2 = '*/' &, \in_apost &, \in_quote then do level = level - 1 tempout = tempout || '*/' rxdata = substr(rxdata, 3) end /***************************************************************** * A special character has to be translated and highlighted * *****************************************************************/ when sc > 0 &, sh > 0 &, \in_com &, \in_apost &, \in_quote then do tempout = tempout || col_spc || word(special_html, sc)'</em>' rxdata = substr(rxdata, 2) end /***************************************************************** * A special character has to be translated * *****************************************************************/ when sc > 0 then do tempout = tempout || word(special_html, sc) rxdata = substr(rxdata, 2) end /***************************************************************** * A special character has to be highlighted * *****************************************************************/ when sh > 0 &, \in_com &, \in_apost &, \in_quote then do tempout = tempout || col_spc || c1'</em>' rxdata = substr(rxdata, 2) end /***************************************************************** * It's a keyword * *****************************************************************/ when keyword.kw = 1 &, \in_com &, \in_apost &, \in_quote then do kw = left(rxdata, length(kw)) tempout = tempout || col_key || kw'</em>' rxdata = substr(rxdata, length(kw) + 1) end /***************************************************************** * It's a left parenthesis * *****************************************************************/ when c1 = '(' &, \in_com &, \in_apost &, \in_quote then do paren = (paren + 1) // 5 tempout = tempout || col_par.paren'(</em>' rxdata = substr(rxdata, 2) end /***************************************************************** * It's a right parenthesis * *****************************************************************/ when c1 = ')' &, \in_com &, \in_apost &, \in_quote then do tempout = tempout || col_par.paren')</em>' paren = (paren + 4) // 5 rxdata = substr(rxdata, 2) end /***************************************************************** * Anything else * *****************************************************************/ otherwise do tempout = tempout || c1 rxdata = substr(rxdata, 2) end end /******************************************************************* * Get more data * *******************************************************************/ if length(rxdata) < 80 then do r = r + 1 rxdata = rxdata || rxdata.r end /******************************************************************* * Append data to final result * *******************************************************************/ if length(tempout) > 512 then do htmlout = htmlout || tempout tempout = '' end end htmlout = htmlout || tempout return /*********************************************************************** * BUILD_LIST_OF_KEYWORDS: * * * * This procedure loads the list of REXX keywords * ***********************************************************************/ build_list_of_keywords: keyword. = 0 /********************************************************************* * These three variables have values| * *********************************************************************/ drop rc sigl source /********************************************************************* * Keyword Instructions * *********************************************************************/ keyword.ADDRESS = 1 keyword.ARG = 1 keyword.BY = 1 keyword.CALL = 1 keyword.DO = 1 keyword.DROP = 1 keyword.ELSE = 1 keyword.END = 1 keyword.ERROR = 1 keyword.EXIT = 1 keyword.EXPOSE = 1 keyword.FOR = 1 keyword.FOREVER = 1 keyword.FAILURE = 1 keyword.HALT = 1 keyword.IF = 1 keyword.INTERPRET = 1 keyword.ITERATE = 1 keyword.LEAVE = 1 keyword.NOP = 1 keyword.NUMERIC = 1 keyword.OFF = 1 keyword.OTHERWISE = 1 keyword.ON = 1 keyword.PARSE = 1 keyword.PROCEDURE = 1 keyword.PULL = 1 keyword.PUSH = 1 keyword.QUEUE = 1 keyword.RC = 1 keyword.RESULT = 1 keyword.RETURN = 1 keyword.SAY = 1 keyword.SCIENTIFIC = 1 keyword.SELECT = 1 keyword.SIGL = 1 keyword.SIGNAL = 1 keyword.SOURCE = 1 keyword.SYNTAX = 1 keyword.THEN = 1 keyword.TO = 1 keyword.TRACE = 1 keyword.UNTIL = 1 keyword.UPPER = 1 keyword.VAR = 1 keyword.VERSION = 1 keyword.WHEN = 1 keyword.WHILE = 1 keyword.WITH = 1 /********************************************************************* * Built-in Functions * *********************************************************************/ keyword.ABBREV = 1 keyword.ABS = 1 keyword.ADDRESS = 1 keyword.ARG = 1 keyword.BITAND = 1 keyword.BITOR = 1 keyword.BITXOR = 1 keyword.B2X = 1 keyword.CENTER = 1 keyword.CENTRE = 1 keyword.COMPARE = 1 keyword.CONDITION = 1 keyword.COPIES = 1 keyword.C2D = 1 keyword.C2X = 1 keyword.DATATYPE = 1 keyword.DATE = 1 keyword.DELSTR = 1 keyword.DELWORD = 1 keyword.DIGITS = 1 keyword.D2C = 1 keyword.D2X = 1 keyword.ERRORTEXT = 1 keyword.FIND = 1 keyword.FORM = 1 keyword.FORMAT = 1 keyword.FUZZ = 1 keyword.INDEX = 1 keyword.INSERT = 1 keyword.JUSTIFY = 1 keyword.LASTPOS = 1 keyword.LEFT = 1 keyword.LENGTH = 1 keyword.LINESIZE = 1 keyword.MAX = 1 keyword.MIN = 1 keyword.OVERLAY = 1 keyword.POS = 1 keyword.QUEUED = 1 keyword.RANDOM = 1 keyword.REVERSE = 1 keyword.RIGHT = 1 keyword.SIGN = 1 keyword.SOURCELINE = 1 keyword.SPACE = 1 keyword.STRIP = 1 keyword.SUBSTR = 1 keyword.SUBWORD = 1 keyword.SYMBOL = 1 keyword.TIME = 1 keyword.TRACE = 1 keyword.TRANSLATE = 1 keyword.TRUNC = 1 keyword.USERID = 1 keyword.VALUE = 1 keyword.VERIFY = 1 keyword.WORD = 1 keyword.WORDINDEX = 1 keyword.WORDLENGTH = 1 keyword.WORDPOS = 1 keyword.WORDS = 1 keyword.XRANGE = 1 keyword.X2B = 1 keyword.X2C = 1 keyword.X2D = 1 /********************************************************************* * TSO/E External Functions * *********************************************************************/ keyword.EXTERNALS = 1 keyword.FIND = 1 keyword.GETMSG = 1 keyword.INDEX = 1 keyword.JUSTIFY = 1 keyword.LINESIZE = 1 keyword.LISTDSI = 1 keyword.MSG = 1 keyword.MVSVAR = 1 keyword.OUTTRAP = 1 keyword.PROMPT = 1 keyword.SETLANG = 1 keyword.STORAGE = 1 keyword.SYSCPUS = 1 keyword.SYSDSN = 1 keyword.SYSVAR = 1 keyword.USERID = 1 /********************************************************************* * DBCS Functions - only DBCS is HILITE'd on ISPF 5.7.0000 * *********************************************************************/ keyword.DBADJUST = 1 keyword.DBBRACKET = 1 keyword.DBCENTER = 1 keyword.DBCJUSTIFY = 1 keyword.DBCS = 1 keyword.DBLEFT = 1 keyword.DBRIGHT = 1 keyword.DBRLEFT = 1 keyword.DBRRIGHT = 1 keyword.DBTODBCS = 1 keyword.DBTOSBCS = 1 keyword.DBUNBRACKET = 1 keyword.DBVALIDATE = 1 keyword.DBWIDTH = 1 /********************************************************************* * TSO/E REXX Commands - not HILITE'd on ISPF 5.7.0000... * *********************************************************************/ keyword.DELSTACK = 1 keyword.DROPBUF = 1 keyword.EXECIO = 1 keyword.EXECUTIL = 1 keyword.HE = 1 keyword.HI = 1 keyword.HT = 1 keyword.MAKEBUF = 1 keyword.NEWSTACK = 1 keyword.QBUF = 1 keyword.QELEM = 1 keyword.QSTACK = 1 keyword.RT = 1 keyword.SUBCOM = 1 keyword.TE = 1 keyword.TS = 1 return