/* : a quick and dirty REXX tag to highlight REXX code*/ /* */ /* Parameters: lineno=[YES|1|NO|0] */ /* */ /* Version: 1.0 */ /* */ /* Author: (c) Jose Maria Blasco */ /* */ /* This software is subject to the terms of the Common Public License. You */ /* must accept the terms of this license to use this software. Refer to */ /* the license at the following URL for more information: */ /* http://oss.software.ibm.com/developerworks/opensource/CPLv1.0.htm */ /* */ /* Description: A quick-and-dirty REXX code highlighter which is used */ /* in the REXXTAGS tutorial (and elsewhere on the REXXTAGS site, */ /* http://www.rexxtags.org ). I wrote it in 30 minutes to test code */ /* highlighting, and invested later some more time so that the code is */ /* readable and has some documentation */ /* */ /* Disclaimer: I wrote this program for my own needs (mainly, for the */ /* REXXTAGS tutorial). It is NOT a complete REXX highlighter, nor does it */ /* handle all syntactical cases, stop words or BIFs. Use at your own risk. */ /* */ /* Modifications: */ /* */ /* Date Author Description */ /* ---------- ----------- --------------------------------------------------- */ /* 2002/12/04 J.M.Blasco v1.0 Initial release */ Parse arg verb, parms, body /* Standard 'parse' for tags */ If verb == 'QUERY BODY CONTENT' Then Return 'TAGDEPENDENT' /* Process tag body ourselves */ If verb == 'START' then Return '' /* No output at start tag */ If verb == 'BODY' Then Do /* Standard body processing */ Interpret body /* Get the tag body into.. */ Signal Body /* ..the 'body.' stem */ End /* */ If verb == 'END' then Return '' /* Nothing output at end tag */ Return 0 /* Return 0 for all other calls */ /*----------------------------------------------------------------------------*/ /* Body processing routine. Here we do the actual parsing */ /*----------------------------------------------------------------------------*/ Body: /* We first fill the 'res.' and 'fun.' stems with reserved words and BIF.. */ /* ..names. */ Call SetReserved Call SetFunctions /* We now parse the parameter string and check for the presence of a.. */ /* ..'lineno' parameter. Note that we do little actual verification of.. */ /* ..correctness of the parameter. */ Parse var parms 'lineno="'lineno'"' If Translate(lineno) == "YES" Then lineno = 1 If lineno \= 1 Then lineno = 0 printlinenos = lineno /* Variable 'r' will hold the constructed return value (i.e. the.. */ /* ..highlighted, parsed program). */ r = '' lineno = 1 /* For line number counting */ diff = 0 /* The following code allows.. */ If body.1 = '' Then Do /* ..for the first line to be.. */ lineno = 2 /* ..a blank line. */ diff = 1 /* 'diff' will be subtracted.. */ End /* ..from 'lineno' below. */ /*--------------------------------------------------------------------------*/ /* We iterate over all body lines, with a few tricks */ /*--------------------------------------------------------------------------*/ Do lineno = lineno To body.0 /* We parse all 'body.' lines. */ l = body.lineno /* Get the line in 'l'. Don't.. */ If l = '' & lineno = body.0 Then Leave /* print the last line if blank */ /*------------------------------------------------------------------------*/ /* Main loop. Parse one line; parsed results will go into 'lr' */ /*------------------------------------------------------------------------*/ l = change(l,'&','&') /* Don't mess with <, > and & .. */ l = change(l,'<','<') /* in HTML code. */ l = change(l,'>','>') l = change(l,' ','  ') l = change(l,' ','  ') lr = '' /* 'lr' gets the parsed part. */ Do while l <> '' /* Loop until the line is parsed.*/ /* First take care of starting blanks */ nb = Verify(l,' ') If nb > 0 & left(l,1) == ' ' Then Do lr = lr||Copies(' ',nb-1) l = Strip(l,'L') End /* Get a token (word) from the (rest of the) line */ Parse var l w' 'l /* Built-in Functions */ If pos('(',w) > 1 Then Do Parse var w fn'('rest ufn = Translate(fn) If fun.ufn Then Do lr = lr''fn'(' l = rest' 'l Iterate End End /* 'Reserved' words */ uw = Translate(w) If res.uw Then Do lr = lr''w' ' Iterate End /* Comments */ If left(w,2) == '/*' Then Do If Pos('*/',SubStr(w,3)' 'l) > 0 Then Do Parse Value SubStr(w,3)' 'l With b'*/'l lr = lr'/*'||b'*/' Iterate End End /* This code is to separate expressions from abuttal strings */ p1 = Pos('"',w) p2 = Pos("'",w) If p1 > 1 | p2 > 1 Then Do If p1 = 0 | (p2 > 0 & p2 < p1) Then Do lr = lr||Left(w,p2-1) w = Substr(w,p2) End Else Do lr = lr||Left(w,p1-1) w = Substr(w,p1) End End /* Handle strings */ c = left(w,1) If c = '"' | c = "'" Then Do start = 2 w = w' 'l l = '' Do Forever p = Pos(c,w,start) If p = 0 Then p = Length(w) /* Unended string */ If SubStr(w,p+1,1) = c Then start = p+2 Else Leave End l = Substr(w,p+1) lr = lr''left(w,p)'' Iterate End lr = lr||w' ' End If printlinenos Then lr = ''Change(Right(lineno-diff,3),' ',' ')':'lr If r = '' then r = '
'lr Else r = r'00'x'
'lr End Return ''r'' /* This is the list of 'reserved' words. Extend as needed */ SetReserved: res. = 0 res.if = 1 res.then = 1 res.else = 1 res.do = 1 res.to = 1 res.end = 1 res.leave = 1 res.iterate = 1 res.say = 1 res.parse = 1 res.with = 1 res.select = 1 res.when = 1 res.otherwise = 1 res.call = 1 res.signal = 1 res.arg = 1 res.return = 1 res.procedure = 1 res.expose = 1 res.var = 1 res.value = 1 Return /* This is the list of built-in functions. Extend as needed */ SetFunctions: fun. = 0 fun.substr = 1 fun.left = 1 fun.right = 1 fun.arg = 1 fun.reverse = 1 fun.pos = 1 fun.translate = 1 fun.word = 1 fun.length = 1 fun.date = 1 Return /* The following internal function is from Mike Cowlishaw */ /* (posted in comp.lang.rexx) */ /* Internal function: CHANGE(string,old,new) */ /* */ /* (Like XEDIT "C/old/new/1 *") */ /* */ /* Changes all occurrences of "old" in "string" to "new". */ /* If "old"=='', then "new" is prefixed to "string". MFC */ Change: procedure expose changed parse arg string, old, new if old=='' then do; changed=1; return new||string; end out=''; changed=0 do while pos(old,string)\=0 parse var string prefix (old) string out=out||prefix||new changed=changed+1 end return out||string