Syntax highlighting using Mediawiki formatting: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|Julia}}: change comment)
m (→‎{{header|Wren}}: Minor change)
(15 intermediate revisions by 4 users not shown)
Line 7: Line 7:
'''bold-word''' and ''italic-word'' appears as '''bold-word''' and ''italic-word''.
'''bold-word''' and ''italic-word'' appears as '''bold-word''' and ''italic-word''.
<br><br>
<br><br>
This could be used to provide simple syntax-highlighting without the use of the relatively more expensive &lt;syntachighlight&gt; tags or for languages not currently supported by Pygments.
This could be used to provide simple syntax-highlighting without the use of the relatively more expensive &lt;syntaxhighlight&gt; tags or for languages not currently supported by Pygments.
A few languages on Rosetta Code are currently using schemes like this.
A few languages on Rosetta Code are currently using schemes like this.
<br>
<br>
Line 35: Line 35:
Handles upper-stropping Algol 68 sources (as used by ALGOL 68G and most other compilers).
Handles upper-stropping Algol 68 sources (as used by ALGOL 68G and most other compilers).


''# Convert an upper-stropped Algol 68 source to "wiki" format #''
''CO Convert an upper-stropped Algol 68 source to "wiki" format''
''# each line is preceeded by a space, #''
'' each line is preceeded by a space,''
''# bold words are enclosed in &apos;&apos;&apos; and &apos;&apos;&apos; and comments in &apos;&apos; and &apos;&apos; #''
'' bold words are enclosed in &apos;&apos;&apos; and &apos;&apos;&apos; and comments in &apos;&apos; and &apos;&apos;''
''# &apos;, &amp;, &lt; and &gt; are converted to &amp;apos; &amp;amp; &amp;lt; and &amp;gt; #''
'' &apos;, &amp;, &lt; and &gt; are converted to &amp;apos; &amp;amp; &amp;lt; and &amp;gt;''
''# everything else if output as is #''
'' everything else if output as is''
'' quote-stropping, point-stropping and res-stropping is not suppoered''
''# the source is read from stand in and written to stand out #''
'' the source is read from stand in and written to stand out''
''# the last line in the file must end with a newline #''
'' the last line in the file must end with a newline''
''# { and } are assumed to be alternatives for ( and ), if { } should be #''
'' { and } are assumed to be alternatives for ( and ), if { } should be''
''# treated as comments ( as in ALGOL68RS/algol68toc ) #''
''# change rs style brief comments to TRUE #''
'' treated as comments ( as in ALGOL68RS/algol68toc )''
'' change rs style brief comments to TRUE''
''CO''
'''BEGIN'''
'''BEGIN'''
'''BOOL''' in string := '''FALSE''';
'''BOOL''' in brief comment := '''FALSE''';
'''INT''' rs comment depth := 0;
'''STRING''' comment delimiter := "";
''# TRUE if {} delimits a nestable brief comment, as in ALGOL 68RS and #''
''# TRUE if {} delimits a nestable brief comment, as in ALGOL 68RS and #''
Line 59: Line 66:
);
);
'''CHAR''' nl = '''REPR''' 10; ''# newline character #''
'''CHAR''' nl = '''REPR''' 10; ''# newline character #''
'''INT''' error count := 0; ''# number of errors reported #''
'''STRING''' line := nl; ''# current source line #''
'''STRING''' line := nl; ''# current source line #''
'''INT''' pos := '''LWB''' line; ''# current position in line #''
'''INT''' pos := '''LWB''' line; ''# current position in line #''
'''CHAR''' c := " "; ''# current source character #''
'''CHAR''' c := " "; ''# current source character #''
'''PROC''' error = ( '''STRING''' message )'''VOID''': ''# reports an error #''
'''BEGIN'''
error count +:= 1;
print( ( newline, newline, "**** ", message, newline ) )
'''END''' ''# error #'' ;
''# reports an unterminated construct ( e.g. string, comment ) #''
'''PROC''' unterminated = ( '''STRING''' construct )'''VOID''': error( "Unterminated " + construct );
'''PROC''' next char = '''VOID''': ''# gets the next source character, stores it in c #''
'''PROC''' next char = '''VOID''': ''# gets the next source character, stores it in c #''
'''IF''' pos &lt;= '''UPB''' line '''THEN'''
'''IF''' pos &lt;= '''UPB''' line '''THEN'''
Line 81: Line 80:
'''THEN'''
'''THEN'''
line +:= nl; ''# have another line #''
line +:= nl; ''# have another line #''
pos := '''LWB''' line;
c := line[ pos := '''LWB''' line ];
c := line[ pos ];
pos +:= 1
pos +:= 1
'''ELSE'''
'''ELSE'''
Line 89: Line 87:
'''FI''' ''# next char #'' ;
'''FI''' ''# next char #'' ;
'''PROC''' out char = ( '''CHAR''' ch )'''VOID''': ''# conveerts and outputs ch #''
'''PROC''' out char = ( '''CHAR''' ch )'''VOID''': ''# conveerts and outputs ch #''
'''IF''' ch = nl '''THEN''' print( ( newline, " " ) )
'''IF''' ch = nl '''THEN'''
'''IF''' '''NOT''' in brief comment '''AND''' rs comment depth = 0 '''AND''' comment delimiter = "" '''THEN'''
print( ( newline, " " ) ) ''# newline not in a comment #''
'''ELSE''' ''# newline in a comment #''
italic delimiter; print( ( newline, " " ) ); italic delimiter
'''FI'''
'''ELIF''' ch = "&lt;" '''THEN''' print( ( "&amp;lt;" ) )
'''ELIF''' ch = "&lt;" '''THEN''' print( ( "&amp;lt;" ) )
'''ELIF''' ch = "&gt;" '''THEN''' print( ( "&amp;gt;" ) )
'''ELIF''' ch = "&gt;" '''THEN''' print( ( "&amp;gt;" ) )
Line 96: Line 99:
'''ELSE''' print( ch )
'''ELSE''' print( ch )
'''FI''' ''# out char #'' ;
'''FI''' ''# out char #'' ;
''# outputs the current character and gets the next #''
'''PROC''' out and next char = '''VOID''': '''BEGIN''' out char( c ); next char '''END''';
''# outputs a wiki start/end italic delimiter #''
''# outputs a wiki start/end italic delimiter #''
'''PROC''' italic delimiter = '''VOID''': print( ( "&apos;&apos;" ) );
'''PROC''' italic delimiter = '''VOID''': print( ( "&apos;&apos;" ) );
''# outputs a wiki start/end bold delimiter #''
'''PROC''' bold delimiter = '''VOID''': print( ( "&apos;&apos;&apos;" ) );
''# returns TRUE if the current character is a string delimiter #''
'''PROC''' have string delimiter = '''BOOL''': c = """";
''# returns TRUE if the current character can start a bold word #''
''# returns TRUE if the current character can start a bold word #''
'''PROC''' have bold = '''BOOL''': c &gt;= "A" '''AND''' c &lt;= "Z";
'''PROC''' have bold = '''BOOL''': c &gt;= "A" '''AND''' c &lt;= "Z";
''# outputs a brief comment to stand out #''
''# end char is the closing delimiter, #''
''# nested char is the opening delimiter for nestable brief comments #''
''# if nested char is blank, the brief comment does not nest #''
''# this handles ALGOL 68RS and algol68toc style {} comments #''
'''PROC''' copy brief comment = ( '''CHAR''' end char, '''CHAR''' nested char )'''VOID''':
'''BEGIN'''
out char( c );
'''WHILE''' next char;
'''NOT''' at eof '''AND''' c /= end char
'''DO'''
'''IF''' c = nested char '''AND''' nested char /= " " '''THEN'''
''# nested brief comment #''
copy brief comment( end char, nested char )
'''ELSE'''
''# notmal comment char #''
out char( c )
'''FI'''
'''OD''';
'''IF''' at eof '''THEN'''
''# unterminated comment #''
unterminated( """" + end char + """ comment" );
c := end char
'''FI''';
out char( c );
next char
'''END''' ''# copy brief comment #'' ;
'''PROC''' copy string = '''VOID''': ''# outputs a string denotation from the source #''
'''WHILE''' have string delimiter '''DO''' ''# within a string denotation, #''
'''WHILE''' out char( c ); ''# "" denotes the " character #''
next char;
'''NOT''' at eof '''AND''' '''NOT''' have string delimiter
'''DO''' '''SKIP''' '''OD''';
'''IF''' '''NOT''' have string delimiter '''THEN'''
unterminated( "string" );
c := """"
'''FI''';
out char( c );
next char
'''OD''' ''# copy string #'' ;
'''PROC''' get bold word = '''STRING''': ''# gets a bold word from then source #''
'''PROC''' get bold word = '''STRING''': ''# gets a bold word from then source #''
'''BEGIN'''
'''BEGIN'''
Line 150: Line 111:
result
result
'''END''' ''# get bold word #'' ;
'''END''' ''# get bold word #'' ;
'''PROC''' copy to bold = '''STRING''': ''# copies the source to the output #''
'''IF''' at eof ''# until a bold word is encountered #''
'''THEN''' ""
'''ELSE''' '''STRING''' result := "";
'''WHILE''' out char( c );
next char;
'''NOT''' at eof
'''AND''' '''NOT''' have bold
'''DO''' '''SKIP''' '''OD''';
'''IF''' '''NOT''' at eof '''THEN''' result := get bold word '''FI''';
result
'''FI''' ''# copy to bold #'' ;
'''PROC''' bold word or comment = '''VOID''': ''# handles a bold COMMENT #''
'''IF''' '''STRING''' bold word := get bold word; ''# or other bold word #''
bold word = "CO" '''OR''' bold word = "COMMENT"
'''THEN'''
italic delimiter; ''# have a bold comment #''
'''STRING''' delimiter = bold word;
'''WHILE''' print( ( bold word ) );
bold word := copy to bold;
'''NOT''' at eof
'''AND''' bold word /= delimiter
'''DO''' '''SKIP''' '''OD''';
'''IF''' at eof '''THEN'''
unterminated( """" + delimiter + """ comment" )
'''FI''';
print( ( delimiter ) );
italic delimiter
'''ELSE''' ''# some other bold word #''
bold delimiter;
print( ( bold word ) );
bold delimiter
'''FI''' ''# bold word or comment #'' ;
''# copy the source to stand out, conveerting to wiki format #''
''# copy the source to stand out, conveerting to wiki format #''
next char;
next char;
'''WHILE''' '''NOT''' at eof '''DO'''
'''WHILE''' '''NOT''' at eof '''DO'''
'''IF''' c = "#" '''THEN''' ''# brief comment #''
'''IF''' in string '''THEN''' ''# currently in a string #''
in string := c /="""";
out and next char
'''ELIF''' in brief comment '''THEN''' ''# currently in a brief comment #''
in brief comment := c /= "#";
out and next char;
'''IF''' '''NOT''' in brief comment '''THEN''' italic delimiter '''FI'''
'''ELIF''' rs comment depth &gt; 0 '''THEN''' ''# currently in a nesting {...} comment #''
'''IF''' c = "}" '''THEN''' rs comment depth -:= 1 '''FI''';
out and next char;
'''IF''' rs comment depth &lt; 1 '''THEN''' italic delimiter '''FI'''
'''ELIF''' comment delimiter /= "" '''THEN''' ''# in a CO/COMMENT comment #''
'''IF''' '''NOT''' have bold '''THEN'''
out and next char ''# haven&apos;t reached a bold word #''
'''ELSE'''
'''STRING''' word = get bold word; ''# at the start of a bold word #''
print( ( word ) );
'''IF''' word = comment delimiter '''THEN'''
''# reached the end of the comment #''
italic delimiter;
comment delimiter := ""
'''FI'''
'''FI'''
'''ELIF''' c = """" '''THEN''' ''# start of a string or character denotation #''
out and next char;
in string := '''TRUE'''
'''ELIF''' c = "#" '''THEN''' ''# start of a brief comment such as this one #''
italic delimiter;
italic delimiter;
copy brief comment( "#", " " );
out and next char;
italic delimiter
in brief comment := '''TRUE'''
'''ELIF''' c = "{" '''AND''' rs style brief comments '''THEN'''
'''ELIF''' c = "{" '''AND''' rs style brief comments '''THEN''' ''# nestable brief #''
''# nestable brief comment ( ALGOL 68RS and algol68toc ) #''
italic delimiter; ''# comment ( ALGOL 68RS and algol68toc ) #''
italic delimiter;
out and next char;
copy brief comment( "}", "{" );
rs comment depth := 1
italic delimiter
'''ELIF''' have string delimiter '''THEN''' ''# STRING or CHAR denotation #''
copy string
'''ELIF''' have bold '''THEN''' ''# have a bold word #''
'''ELIF''' have bold '''THEN''' ''# have a bold word #''
bold word or comment
'''STRING''' word = get bold word;
'''ELSE'''
'''IF''' word /= "CO" '''AND''' word /= "COMMENT" '''THEN'''
''# anything else #''
print( ( "&apos;&apos;&apos;", word, "&apos;&apos;&apos;" ) ) ''# non-comment bold word #''
out char( c );
'''ELSE'''
italic delimiter; ''# start of a bold comment #''
next char
print( ( word ) );
comment delimiter := word
'''FI'''
'''ELSE''' ''# anything else #''
out and next char
'''FI'''
'''FI'''
'''OD''';
'''OD''';
'''IF''' in string '''THEN''' print( ( "**** unterminated string", newline ) )
'''IF''' error count &gt; 0 '''THEN'''
'''ELIF''' in brief comment '''THEN''' print( ( "**** unterminated brief comment", newline ) )
''# had errors processing the source #''
'''ELIF''' rs comment depth &gt; 0 '''THEN''' print( ( "**** unterminated {...} comment", newline ) )
print( ( "**** ", whole( error count, 0 ), " errors", newline ) )
'''ELIF''' comment delimiter /= "" '''THEN''' print( ( "**** unterminated ", comment delimiter, newline ) )
'''FI'''
'''FI'''
'''END'''
'''END'''


=={{header|ALGOL W}}==

'''begin''' ''comment syntax highlight an Algol W source using Mediawiki formatting''
'' the source is read from standard input and written to standard output''
'' ;''
''% Algol W strings are limited to 256 characters in length so source lines %''
''% are limited to 256 characters %''
'''integer''' lineWidth, errorCount, lowerA, upperA, linePos, kwMax;
'''integer''' MAX_TOKEN_LENGTH;
'''string'''(1) nl;
'''string'''(256) line;
'''string'''(1) currChar, commentEnd;
'''string'''(9) '''array''' kw ( 1 :: 64 );
'''logical''' inString, inComment;
''% returns true if currChar is in the inclusive range low to high, false otherwise %''
'''logical''' '''procedure''' range( '''string'''(1) '''value''' low, high ) ; currChar &gt;= low '''and''' currChar &lt;= high;
'''procedure''' nextChar ; ''% gets the next source character %''
'''if''' linePos = lineWidth '''then''' '''begin'''
currChar := nl;
linePos := linePos + 1
'''end'''
'''else''' '''if''' linePos &gt; lineWidth '''then''' '''begin'''
readcard( line );
lineWidth := 256;
'''while''' lineWidth &gt; 1 '''and''' line( lineWidth - 1 // 1 ) = " " '''do''' lineWidth := lineWidth - 1;
linePos := 1;
currChar := line( 0 // 1 )
'''end'''
'''else''' '''begin'''
currChar := line( linePos // 1 );
linePos := linePos + 1
'''end''' nextChar ;
''% returns true if the current character can start an identifier, false otherwise %''
'''logical''' '''procedure''' identifierStartChar ; range( "a", "z" ) '''or''' range( "A", "Z" );
''% returns true if the current character can be pat of an identifier, false otherwise %''
'''logical''' '''procedure''' identifierChar ; identifierStartChar '''or''' range( "0", "9" ) '''or''' currChar = "_";
'''procedure''' outAndNextChar ; '''begin''' ''% output currChar and get the next %''
'''if''' currChar = "&apos;" '''then''' writeon( "&amp;apos;" )
'''else''' '''if''' currChar = "&amp;" '''then''' writeon( "&amp;amp;" )
'''else''' '''if''' currChar = "&lt;" '''then''' writeon( "&amp;lt;" )
'''else''' '''if''' currChar = "&gt;" '''then''' writeon( "&amp;gt;" )
'''else''' '''if''' currChar = nl '''then''' '''begin'''
'''if''' inComment '''then''' writeon( "&apos;&apos;" );
write( " " );
'''if''' inComment '''then''' writeon( "&apos;&apos;" )
'''end'''
'''else''' writeon( currChar );
nextChar
'''end''' outAndNextChar ;
'''procedure''' identifierOrKeyword ; '''begin''' ''% handle an indentifier or keyword %''
'''string'''(9) word, lWord;
'''integer''' wLength;
''% recursive keyword binary search %''
'''logical''' '''procedure''' isKeyword ( '''integer''' '''value''' low, high ) ;
'''if''' high &lt; low '''then''' '''false'''
'''else''' '''begin'''
'''integer''' mid;
mid := ( low + high ) '''div''' 2;
'''if''' kw( mid ) &gt; lWord '''then''' isKeyword( low, mid - 1 )
'''else''' '''if''' kw( mid ) = lWord '''then''' '''true'''
'''else''' isKeyword( mid + 1, high )
'''end''' binarySearchR ;
wLength := 0;
'''for''' chPos := 0 '''until''' 8 '''do''' '''begin'''
'''if''' identifierChar '''then''' '''begin'''
word( chPos // 1 ) := currChar;
lWord( chPos // 1 ) := '''if''' range( "A", "Z" ) '''then''' code( ( decode( currChar ) - upperA ) + lowerA )
'''else''' currChar;;
wLength := wLength + 1;
nextChar
'''end'''
'''else''' '''begin'''
lWord( chPos // 1 ) := " ";
word( chPos // 1 ) := " "
'''end''' if_identifierChar__
'''end''' for_chPos ;
'''if''' identifierChar '''then''' '''begin'''
''% all keywords are &lt;= 9 characters long so this must be an identifier %''
writeon( word );
'''while''' identifierChar '''do''' outAndNextChar
'''end'''
'''else''' '''if''' lWord = "comment" '''then''' '''begin'''
writeon( "&apos;&apos;comment" );
commentEnd := ";";
inComment := '''true'''
'''end'''
'''else''' '''if''' isKeyword( 1, kwMax ) '''then''' '''begin'''
writeon( "&apos;&apos;&apos;" );
'''for''' chPos := 0 '''until''' wLength - 1 '''do''' writeon( word( chPos // 1 ) );
writeon( "&apos;&apos;&apos;" )
'''end'''
'''else''' '''begin''' ''% identifier %''
'''for''' chPos := 0 '''until''' wLength - 1 '''do''' writeon( word( chPos // 1 ) )
'''end''' if_various_words
'''end''' identifierOrKeyword ;
s_w := 0; i_w := 1; ''% output formarting %''
MAX_TOKEN_LENGTH := 256;
nl := code( 10 );
lowerA := decode( "a" );
upperA := decode( "A" );
''% allow the program to continue after reaching end-of-file %''
ENDFILE := EXCEPTION( '''false''', 1, 0, '''false''', "EOF" );
''% ensure the first call to nextChar reads the first line %''
lineWidth := 256;
linePos := lineWidth + 1;
currChar := " ";
'''begin''' ''% keywords %''
'''procedure''' K ( '''string'''(9) '''value''' kwStr ) ; '''begin'''
kwMax := kwMax + 1;
kw( kwMax ) := kwStr
'''end''' K ;
kwMax := 0;
K("abs");K("algol");K("and");K("array");K("assert");K("begin");K("bits");
K("case");K("complex");K("div");K("do");K("else");K("end");K("false");
K("for");K("fortran");K("go");K("goto");K("if");K("integer");K("is");
K("logical");K("long");K("not");K("null");K("of");K("or");
K("procedure");K("real");K("record");K("reference");K("rem");K("result");
K("shl");K("short");K("shr");K("step");K("string");
K("then");K("to");K("true");K("until");K("value");K("while")
'''end''' keywords ;
inString := inComment := '''false''';
outAndNextChar;
'''while''' '''not''' XCPNOTED(ENDFILE) '''do''' '''begin'''
'''if''' inString '''then''' '''begin''' ''% in a string %''
inString := currChar '''not''' = """";
outAndNextChar;
'''end'''
'''else''' '''if''' inComment '''then''' '''begin''' ''% in a comment %''
inComment := currChar '''not''' = ";" '''and''' currChar '''not''' = commentEnd;
outAndNextChar;
'''if''' '''not''' inComment '''then''' writeon( "&apos;&apos;" );
'''end'''
'''else''' '''if''' identifierStartChar '''then''' identifierOrKeyword
'''else''' '''if''' currChar = """" '''then''' '''begin''' ''% string literal %''
outAndNextChar;
inString := '''true'''
'''end'''
'''else''' '''if''' currChar = "%" '''then''' '''begin''' ''% brief comment %''
writeon( "&apos;&apos;" );
commentEnd := "%";
inComment := '''true''';
outAndNextChar
'''end'''
'''else''' outAndNextChar
'''end''' while_not_ar_eof ;
'''if''' inComment '''then''' write( "**** unterminated comment" )
'''else''' '''if''' inString '''then''' write( "**** unterminated string" )
'''end'''.
=={{header|AWK}}==
=={{header|AWK}}==


Line 227: Line 341:
'''BEGIN''' \
'''BEGIN''' \
{
{
''# reserved word list as in gawk and treating getline as reserved ''
''# reserved word list as in gawk and treating getline as reserved ''
kw = "BEGIN/BEGINFILE/END/ENDFILE/" \
kw = "BEGIN/BEGINFILE/END/ENDFILE/" \
Line 241: Line 354:
} ''# BEGIN''
} ''# BEGIN''
{
{
printf( " " );
printf( " " );
line = $0;
line = $0;
Line 269: Line 382:
'''if'''( c == "\\" )
'''if'''( c == "\\" )
{
{
printf( "%s", c );
outAndNextChar();
nextChar();
}
}
printf( "%s", c );
outAndNextChar();
nextChar();
}
}
'''while'''( c != "\"" &amp;&amp; c != "" );
'''while'''( c != "\"" &amp;&amp; c != "" );
Line 290: Line 401:
''# pattern''
''# pattern''
bracketDepth = 0;
bracketDepth = 0;
printf( "%s", c );
outAndNextChar();
nextChar();
'''while'''( c != "" &amp;&amp; ( c != "/" || bracketDepth &gt; 0 ) )
'''while'''( c != "" &amp;&amp; ( c != "/" || bracketDepth &gt; 0 ) )
{
{
'''if'''( c == "\\" || c == "[" )
'''if'''( c == "\\" || c == "[" )
{
{
'''if''' ( c == "[" )
'''if'''( c == "[" )
{
{
bracketDepth ++;
bracketDepth ++;
}
}
printf( "%s", c );
outAndNextChar();
nextChar();
}
}
'''else''' '''if'''( c == "]" )
'''else''' '''if'''( c == "]" )
Line 307: Line 416:
bracketDepth --;
bracketDepth --;
}
}
printf( "%s", c );
outAndNextChar();
nextChar();
}
}
'''if'''( c != "/" )
'''if'''( c != "/" )
Line 339: Line 447:
{
{
''# something else''
''# something else''
printf( "%s", c );
outAndNextChar();
nextChar();
}
}
}
}
Line 347: Line 454:
printf( "\n" );
printf( "\n" );
}
'''function''' outAndNextChar()
{
printf( "%s", c );
nextChar();
}
}
Line 359: Line 472:
{
{
''# at end of line''
''# at end of line''
c = "";
lastC = c = "";
}
}
'''else'''
'''else'''
Line 370: Line 483:
} ''# nextChar''
} ''# nextChar''


=={{header|Julia}}==
=={{header|Julia}}==
''#= Keywords in Julia. Handles two word reserved keywords. #= Also
#= handles nested comments such as this. =# =#
''#= Keywords in Julia. Note that though two word keywords are listed, they ''
=#''
'' only work if the two words are separated by a single space character. ''
'' #= Handles nested comments such as this. =#''
''=#''
'''const''' KEYWORDS = map(
'''const''' KEYWORDS = map(
w -&gt; Regex("^" * w * "\\W"),
w -&gt; Regex("^" * w * "\\W"),
sort(
sort(
[
[
"abstract type",
raw"abstract\s+type",
"baremodule",
"baremodule",
"begin",
"begin",
"break",
"break",
"catch",
"catch",
"const",
"const",
"continue",
"continue",
"do",
"do",
"else",
"else",
"elseif",
"elseif",
"end",
"end",
"export",
"export",
"false",
"false",
"finally",
"finally",
"for",
"for",
"function",
"function",
"global",
"global",
"if",
"if",
"import",
"import",
"in",
"in",
"isa",
"isa",
"let",
"let",
"local",
"local",
"macro",
"macro",
"module",
"module",
"mutable struct",
raw"mutable\s+struct",
"outer",
"outer",
"primitive type",
raw"primitive\s+type",
"quote",
"quote",
"return",
"return",
"struct",
"struct",
"true",
"true",
"try",
"try",
"using",
"using",
"while",
"while",
"where",
"where",
], rev = '''true''', by = length),
], rev = '''true''', by = length),
) ''# reorder to largest first then convert to Regex''
) ''# reorder to largest first then convert to Regex''
""" Find the #= =# delineated comment, including nested versions """
""" Find the #= =# delineated comment, including nested versions """
'''function''' nestedcommentlimits(s::AbstractString, startcomment = "#=", stopcomment = "=#")
'''function''' nestedcommentlimits(s::AbstractString, startcomment = "#=", stopcomment = "=#")
either = Regex("$startcomment|$stopcomment", "sa")
either = Regex("$startcomment|$stopcomment", "sa")
depth, startpos, stoppos = 0, 0, 0
depth, startpos, stoppos = 0, 0, 0
'''for''' (i, m) '''in''' enumerate(eachmatch(either, s))
'''for''' m '''in''' eachmatch(either, s)
'''if''' m.match == startcomment
'''if''' m.match == startcomment
startpos = startpos == 0 ? m.match.offset : startpos
startpos = startpos == 0 ? m.match.offset : startpos
depth += 1
depth += 1
'''else'''
'''else'''
stoppos = max(stoppos + 1, m.match.offset + 2)
stoppos = max(stoppos + 1, m.match.offset + 2)
depth -= 1
depth -= 1
'''end'''
'''end'''
depth &lt;= 0 &amp;&amp; '''break'''
depth &lt;= 0 &amp;&amp; '''break'''
'''end'''
'''end'''
'''return''' startpos, stoppos
'''return''' startpos, stoppos
'''end'''
'''end'''
"""
"""
Given a string, output a string that has been modified by adding surrounding
Given a string, output a string that has been modified by adding surrounding
\'\' or \'\'\' bracketing for syntax highlighting of keywords and comments
\'\' or \'\'\' bracketing for syntax highlighting of keywords and comments
"""
"""
'''function''' partialhighlight(txt)
'''function''' partialhighlight(txt)
outtxt = Char[]
outtxt = Char[]
idx, len = 1, length(txt)
idx, len = 1, length(txt)
'''while''' idx &lt;= len
'''while''' idx &lt;= len
'''if''' !isvalid(txt, idx)
'''if''' !isvalid(txt, idx) ''# exclude internal positions of multibyte Char''
idx += 1
idx += 1
'''continue'''
'''continue'''
'''end'''
'''end'''
c = txt[idx]
c = txt[idx]
'''if''' c == &apos;\\&apos;
'''if''' c == &apos;\\&apos; ''# escape the next char, send as is''
push!(outtxt, c, txt[idx+1])
push!(outtxt, c, txt[idx+1])
idx += 2
idx += 2
'''elseif''' c == &apos;\"&apos;
'''elseif''' c == &apos;\"&apos; ''# quotation start''
'''if''' idx &lt; len - 2 &amp;&amp; c == txt[idx+1] == txt[idx+2]
'''if''' idx &lt; len - 2 &amp;&amp; c == txt[idx+1] == txt[idx+2] ''# """ quotes """''
qlen = findfirst(r"(?<!\\)\"\"\""sa, txt[idx+3:'''end'''])
qlen = findfirst(r"(?<!\\)\"\"\""sa, txt[idx+3:'''end'''])
qlen == nothing &amp;&amp; error("error with terminator of quote at $idx")
qlen == nothing &amp;&amp; error("error with terminator of quote at $idx")
app'''end'''!(outtxt, collect(txt[idx:idx+qlen.stop+2]))
app'''end'''!(outtxt, collect(replace(txt[idx:idx+qlen.stop+2], "\n" =&gt; "\n ")))
idx += qlen.stop + 3
idx += qlen.stop + 3
'''else'''
'''else''' ''# " quote "''
qlen = findfirst(r"(?<!\\)\"", txt[idx+1:'''end'''])
qlen = findfirst(r"(?<!\\)\"", txt[idx+1:'''end'''])
qlen == nothing &amp;&amp; error("error with terminator of quote at $idx")
qlen == nothing &amp;&amp; error("error with terminator of quote at $idx")
app'''end'''!(outtxt, collect(replace(txt[idx:idx+qlen.stop+1], "\n" =&gt; "\n ")))
app'''end'''!(outtxt, collect(replace(txt[idx:idx+qlen.stop+1], "\n" =&gt; "\n ")))
idx += qlen.stop + 2
idx += qlen.stop + 2
'''end'''
'''end'''
'''elseif''' c == &apos;#&apos; &amp;&amp; txt[max(1, idx - 1)] != &apos;&apos;&apos;
'''elseif''' c == &apos;#&apos; &amp;&amp; txt[max(1, idx - 1)] != &apos;&apos;&apos; ''# start comment''
'''if''' idx &lt; len &amp;&amp; txt[idx+1] == &apos;=&apos;
'''if''' idx &lt; len &amp;&amp; txt[idx+1] == &apos;=&apos; ''#= comment =#''
start, stop = nestedcommentlimits(txt[idx:'''end'''])
start, stop = nestedcommentlimits(txt[idx:'''end'''])
s = replace(txt[idx:idx+stop-1], "\n" =&gt; "''\n ''")
s = replace(txt[idx:idx+stop-1], "\n" =&gt; "\n ")
app'''end'''!(outtxt, collect("''$s''"))
app'''end'''!(outtxt, collect("\'\'$s\'\'"))
idx += stop
idx += stop
'''else'''
'''else''' ''# found a line comment, like this comment''
newlinepos = findfirst(==(&apos;\n&apos;), txt[idx+1:'''end'''])
newlinepos = something(findfirst(==(&apos;\n&apos;), txt[idx+1:'''end''']), len - idx)
app'''end'''!(outtxt, collect("\'\'$(txt[idx:idx+newlinepos-1])\'\'"))
newlinepos == nothing &amp;&amp; error("unterminated double quote at $idx")
app'''end'''!(outtxt, collect("''$(txt[idx:idx+newlinepos-1])''"))
idx += newlinepos
idx += newlinepos
'''end'''
'''end'''
'''elseif''' c &apos;a&apos;:&apos;z&apos; ''# lowercase char so check for keyword match''
'''elseif''' c ∈ &apos;a&apos;:&apos;z&apos;
'''for''' (j, reg) '''in''' enumerate(KEYWORDS)
'''for''' (j, reg) '''in''' enumerate(KEYWORDS)
m = match(reg, txt[idx:'''end'''])
m = match(reg, txt[idx:'''end'''])
'''if''' m != nothing
'''if''' m != nothing
wlen = m.match.ncodeunits - 2
wlen = m.match.ncodeunits - 2
app'''end'''!(outtxt, collect("\'\'\'$(txt[idx:idx+wlen])\'\'\'"))
app'''end'''!(outtxt, collect("'''$(txt[idx:idx+wlen])'''"))
idx += wlen + 1
idx += wlen + 1
'''break'''
'''break'''
'''elseif''' j == lastindex(KEYWORDS) ''# no keyword found, send char to output''
'''elseif''' j == lastindex(KEYWORDS)
push!(outtxt, c)
push!(outtxt, c)
idx += 1
idx += 1
'''end'''
'''end'''
'''end'''
'''elseif''' c '''in''' [&apos;&apos;&apos;, &apos;&amp;&apos;, &apos;&lt;&apos;, &apos;&gt;&apos;] ''# \x26 is char & for HTML entity translation''
'''end'''
'''elseif''' c '''in''' [&apos;&apos;&apos;, &apos;&amp;&apos;, &apos;&lt;&apos;, &apos;&gt;&apos;]
s = c == &apos;&apos;&apos; ? "\x26apos;" : c == &apos;&amp;&apos; ? "\x26amp;" : c == &apos;&lt;&apos; ? "\x26lt;" : "\x26gt;"
app'''end'''!(outtxt, collect(s))
s = c == &apos;&apos;&apos; ? "&apos;" : c == &apos;&amp;&apos; ? "&amp;" : c == &apos;&lt;&apos; ? "&lt;" : "&gt;"
app'''end'''!(outtxt, collect(s))
idx += 1
idx += 1
'''else''' ''# nothing special found, so pass char to output and increment index into input''
'''else'''
push!(outtxt, c)
push!(outtxt, c)
idx += 1
'''end'''
c == &apos;\n&apos; &amp;&amp; push!(outtxt, &apos; &apos;)
outtxt['''end'''] == &apos;\n&apos; &amp;&amp; push!(outtxt, &apos; &apos;)
idx += 1
'''end'''
'''end'''
'''end'''
'''return''' String(outtxt)
'''return''' String(outtxt)
'''end'''
'''end'''
println(partialhighlight(read("onedrive/documents/julia programs/test1.jl", String)), "\n")
println(partialhighlight(read(PROGRAM_FILE, String)))


=={{header|Phix}}==
=={{header|Phix}}==
Line 584: Line 694:
'''puts'''(1,out)
'''puts'''(1,out)
{} = '''wait_key'''()
{} = '''wait_key'''()

=={{header|PL/M}}==
{{works with|8080 PL/M Compiler}} ... under CP/M (or an emulator)
Note that PL/M doesn't have in-built I/O or standard libraries, hence the need to define the various BDOS system calls.<br>
As CP/M doesn't have redirection, the source file and output file names must be specified on the command line, e.g. if the source is in D:SYNTAX.PLM and the desired output file is D:SYNTAX.OUT and the program is compiled to D:SYNTAX.COM, then the command:<br>
<code>D:SYNTAX D:SYNTAX.PLM D:SYNTAX.OUT</code><br> will create SYNTAX.OUT as a copy of SYNTAX.PLM with the markup for the highlighting. Note the output file must not exist before running the program.<br>
The output is also echoed to the console.
100H: ''/* SYNTAX HIGHLIGHT A PL/M SOURCE USING MEDIAWIKI MARKUP */''
'''DECLARE''' FALSE '''LITERALLY''' &apos;0&apos;, TRUE '''LITERALLY''' &apos;0FFH&apos;;
'''DECLARE''' NL$CHAR '''LITERALLY''' &apos;0AH&apos;; ''/* NEWLINE: CHAR 10 */''
'''DECLARE''' CR$CHAR '''LITERALLY''' &apos;0DH&apos;; ''/* CARRIAGE RETURN, CHAR 13 */''
'''DECLARE''' EOF$CHAR '''LITERALLY''' &apos;26&apos;; ''/* EOF: CTRL-Z */''
'''DECLARE''' AMP '''LITERALLY''' &apos;026H&apos;; ''/* AMPERSAND */''
'''DECLARE''' LCA '''LITERALLY''' &apos;061H&apos;; ''/* LOWER CASE &apos;A&apos; */''
'''DECLARE''' LCG '''LITERALLY''' &apos;067H&apos;; ''/* LOWER CASE &apos;G&apos; */''
'''DECLARE''' LCL '''LITERALLY''' &apos;06CH&apos;; ''/* LOWER CASE &apos;L&apos; */''
'''DECLARE''' LCM '''LITERALLY''' &apos;06DH&apos;; ''/* LOWER CASE &apos;M&apos; */''
'''DECLARE''' LCO '''LITERALLY''' &apos;06FH&apos;; ''/* LOWER CASE &apos;O&apos; */''
'''DECLARE''' LCP '''LITERALLY''' &apos;070H&apos;; ''/* LOWER CASE &apos;P&apos; */''
'''DECLARE''' LCS '''LITERALLY''' &apos;073H&apos;; ''/* LOWER CASE &apos;S&apos; */''
'''DECLARE''' LCT '''LITERALLY''' &apos;074H&apos;; ''/* LOWER CASE &apos;T&apos; */''
''/* CP/M BDOS SYSTEM CALL, RETURNS A VALUE */''
BDOS: '''PROCEDURE'''( FN, ARG )'''BYTE'''; '''DECLARE''' FN '''BYTE''', ARG '''ADDRESS'''; '''GOTO''' 5; '''END''';
''/* CP/M BDOS SYSTEM CALL, NO RETURN VALUE */''
BDOS$P: '''PROCEDURE'''( FN, ARG ); '''DECLARE''' FN '''BYTE''', ARG '''ADDRESS'''; '''GOTO''' 5; '''END''';
EXIT: '''PROCEDURE'''; '''CALL''' BDOS$P( 0, 0 ); '''END'''; ''/* CP/M SYSTEM RESET */''
PR$CHAR: '''PROCEDURE'''( C ); '''DECLARE''' C '''BYTE'''; '''CALL''' BDOS$P( 2, C ); '''END''';
PR$STRING: '''PROCEDURE'''( S ); '''DECLARE''' S '''ADDRESS'''; '''CALL''' BDOS$P( 9, S ); '''END''';
PR$NL: '''PROCEDURE'''; '''CALL''' PR$STRING( .( 0DH, NL$CHAR, &apos;$&apos; ) ); '''END''';
FL$EXISTS: '''PROCEDURE'''( FCB )'''BYTE'''; ''/* RETURNS TRUE IF THE FILE NAMED IN THE */''
'''DECLARE''' FCB '''ADDRESS'''; ''/* FCB EXISTS */''
'''RETURN''' ( BDOS( 17, FCB ) &lt; 4 );
'''END''' FL$EXISTS ;
FL$OPEN: '''PROCEDURE'''( FCB )'''BYTE'''; ''/* OPEN THE FILE WITH THE SPECIFIED FCB */''
'''DECLARE''' FCB '''ADDRESS''';
'''RETURN''' ( BDOS( 15, FCB ) &lt; 4 );
'''END''' FL$OPEN;
FL$MAKE: '''PROCEDURE'''( FCB )'''BYTE'''; ''/* CREATE AND OPEN THE FILE WITH THE */''
'''DECLARE''' FCB '''ADDRESS'''; ''/* SPECIFIED FCB */''
'''RETURN''' ( BDOS( 22, FCB ) &lt; 4 );
'''END''' FL$MAKE;
FL$READ: '''PROCEDURE'''( FCB )'''BYTE'''; ''/* READ THE NEXT RECORD FROM FCB */''
'''DECLARE''' FCB '''ADDRESS''';
'''RETURN''' ( BDOS( 20, FCB ) = 0 );
'''END''' FL$READ;
FL$WRITE: '''PROCEDURE'''( FCB )'''BYTE'''; ''/* WRITE A RECORD TO FCB */''
'''DECLARE''' FCB '''ADDRESS''';
'''RETURN''' ( BDOS( 21, FCB ) = 0 );
'''END''' FL$WRITE;
FL$CLOSE: '''PROCEDURE'''( FCB )'''BYTE'''; ''/* CLOSE THE FILE WITH THE SPECIFIED FCB */''
'''DECLARE''' FCB '''ADDRESS''';
'''RETURN''' ( BDOS( 16, FCB ) &lt; 4 );
'''END''' FL$CLOSE;
DMA$SET: '''PROCEDURE'''( DMA ); ''/* SET THE DMA BUFFER ADDRESS FOR I/O */''
'''DECLARE''' DMA '''ADDRESS''';
'''CALL''' BDOS$P( 26, DMA );
'''END''' DMA$SET;
''/* I/O USES FILE CONTROL BLOCKS CONTAINING THE FILE-NAME, POSITION, ETC. */''
''/* WHEN THE PROGRAM IS RUN, THE CCP WILL FIRST PARSE THE COMMAND LINE AND */''
''/* PUT THE FIRST PARAMETER IN FCB1, THE SECOND PARAMETER IN FCB2 */''
''/* BUT FCB2 OVERLAYS THE END OF FCB1 AND THE DMA BUFFER OVERLAYS THE END */''
''/* OF FCB2 */''
'''DECLARE''' FCB$SIZE '''LITERALLY''' &apos;36&apos;; ''/* SIZE OF A FCB */''
'''DECLARE''' FCB1 '''LITERALLY''' &apos;5CH&apos;; ''/* ADDRESS OF FIRST FCB */''
'''DECLARE''' FCB2 '''LITERALLY''' &apos;6CH&apos;; ''/* ADDRESS OF SECOND FCB */''
'''DECLARE''' DMA$BUFFER '''LITERALLY''' &apos;80H&apos;; ''/* DEFAULT DMA BUFFER ADDRESS */''
'''DECLARE''' DMA$SIZE '''LITERALLY''' &apos;128&apos;; ''/* SIZE OF THE DMA BUFFER */''
INIT$FCB: '''PROCEDURE'''( FCB ); ''/* INITIALISE A FILE-CONTROL-BLOCK */''
'''DECLARE''' FCB '''ADDRESS''';
'''DECLARE''' F$PTR '''ADDRESS''';
'''DECLARE''' F '''BASED''' F$PTR '''BYTE''', P '''BYTE''';
F$PTR = FCB;
F = 0; ''/* DEFAULT DRIVE */''
'''DO''' F$PTR = FCB + 1 '''TO''' FCB + 11; ''/* NO NAME */''
F = &apos; &apos;;
'''END''';
'''DO''' F$PTR = FCB + 12 '''TO''' FCB + ( FCB$SIZE - 1 ); ''/* OTHER FIELDS */''
F = 0;
'''END''';
'''END''' INIT$FCB;
MOVE$FCB: '''PROCEDURE'''( FROM$FCB, TO$FCB ); ''/* MOVE THE CONTENTS OF AN FCB */''
'''DECLARE''' ( FROM$FCB, TO$FCB ) '''ADDRESS''';
'''DECLARE''' ( F$PTR, T$PTR ) '''ADDRESS''';
'''DECLARE''' F '''BASED''' F$PTR '''BYTE''', T '''BASED''' T$PTR '''BYTE''', P '''BYTE''';
'''CALL''' INIT$FCB( TO$FCB );
F$PTR = FROM$FCB;
T$PTR = TO$FCB;
'''DO''' P = 0 '''TO''' 11; ''/* COPY DRIVE, FILENAME AND EXTENSION */''
T = F;
F$PTR = F$PTR + 1;
T$PTR = T$PTR + 1;
'''END''';
'''END''' MOVE$FCB;
SHOW$FCB: '''PROCEDURE'''( FCB ); ''/* SHOW THE CONTENTS OF AN FCB */''
'''DECLARE''' FCB '''ADDRESS''';
'''DECLARE''' F$PTR '''ADDRESS''';
'''DECLARE''' F '''BASED''' F$PTR '''BYTE''', P '''BYTE''';
F$PTR = FCB;
'''DO''' P = 0 '''TO''' 11; ''/* DRIVE, FILENAME AND EXTENSION */''
'''IF''' P = 9 '''THEN''' '''CALL''' PR$CHAR( &apos;.&apos; );
'''IF''' P = 1 '''THEN''' '''CALL''' PR$CHAR( &apos;:&apos; );
'''CALL''' PR$CHAR( F );
F$PTR = F$PTR + 1;
'''END''';
'''END''' SHOW$FCB;
'''DECLARE''' F$PTR '''ADDRESS''', F$CHAR '''BASED''' F$PTR '''BYTE''';
'''DECLARE''' W$PTR '''ADDRESS''', W$CHAR '''BASED''' W$PTR '''BYTE''';
'''DECLARE''' FCB$OUT$DATA ( FCB$SIZE )'''BYTE''';
'''DECLARE''' OUT$DMA ( DMA$SIZE )'''BYTE''';
'''DECLARE''' OUT$BUFFER '''LITERALLY''' &apos;.OUT$DMA&apos;;
'''DECLARE''' FCB$IN '''LITERALLY''' &apos;FCB1&apos;;
'''DECLARE''' FCB$OUT '''LITERALLY''' &apos;.FCB$OUT$DATA&apos;;
'''DECLARE''' K '''LITERALLY''' &apos;CALL ADDKW&apos;;
'''DECLARE''' KW ( 34 )'''ADDRESS''';
'''DECLARE''' KW$MAX '''BYTE''';
KW$MAX = -1;
ADDKW: '''PROCEDURE'''( ADDR ); ''/* ADDS A KEYWORD TO KW */''
'''DECLARE''' ADDR '''ADDRESS''';
KW( KW$MAX := KW$MAX + 1 ) = ADDR;
'''END''' ADDKW ;
K(.&apos;ADDRESS$&apos;);K(.&apos;AND$&apos;);K(.&apos;BASED$&apos;);K(.&apos;BY$&apos;);K(.&apos;BYTE$&apos;);K(.&apos;CALL$&apos;);
K(.&apos;CASE$&apos;);K(.&apos;DATA$&apos;);K(.&apos;DECLARE$&apos;);K(.&apos;DISABLE$&apos;);K(.&apos;DO$&apos;);K(.&apos;ELSE$&apos;);
K(.&apos;ENABLE$&apos;);K(.&apos;END$&apos;);K(.&apos;EOF$&apos;);K(.&apos;GO$&apos;);K(.&apos;GOTO$&apos;);K(.&apos;HALT$&apos;);
K(.&apos;IF$&apos;);K(.&apos;INITIAL$&apos;);K(.&apos;INTERRUPT$&apos;);K(.&apos;LABEL$&apos;);K(.&apos;LITERALLY$&apos;);
K(.&apos;MINUS$&apos;);K(.&apos;MOD$&apos;);K(.&apos;NOT$&apos;);K(.&apos;OR$&apos;);K(.&apos;PLUS$&apos;);K(.&apos;PROCEDURE$&apos;);
K(.&apos;RETURN$&apos;);K(.&apos;THEN$&apos;);K(.&apos;TO$&apos;);K(.&apos;WHILE$&apos;);K(.&apos;XOR$&apos;);
''/* MOVE THE SECOND FCB TO A NEW PLACE SO IT ISN&apos;T OVERWRITTEN BY FCB1 */''
'''CALL''' MOVE$FCB( FCB2, FCB$OUT );
''/* CLEAR THE PARTS OF FCB1 OVERLAYED BY FCB2 */''
'''DO''' F$PTR = FCB1 + 12 '''TO''' FCB1 + ( FCB$SIZE - 1 );
F$CHAR = 0;
'''END''';
STR$EQUAL: '''PROCEDURE'''( S1, S2 )'''BYTE'''; ''/* RETURN TRUE IF S1 = S2 */''
'''DECLARE''' ( S1, S2 ) '''ADDRESS''';
'''DECLARE''' ( S1$PTR, S2$PTR ) '''ADDRESS''';
'''DECLARE''' C1 '''BASED''' S1$PTR '''BYTE''', C2 '''BASED''' S2$PTR '''BYTE''', SAME '''BYTE''';
S1$PTR = S1; S2$PTR = S2;
'''DO''' '''WHILE''' ( SAME := C1 = C2 ) '''AND''' C1 &lt;&gt; &apos;$&apos; '''AND''' C2 &lt;&gt; &apos;$&apos;;
S1$PTR = S1$PTR + 1; S2$PTR = S2$PTR + 1;
'''END''';
'''RETURN''' SAME;
'''END''' STR$EQUAL ;
IS$WORD$CHAR: '''PROCEDURE'''( CH )'''BYTE'''; ''/* RETURN TRUE IF CH IS PART OF A WORD */''
'''DECLARE''' CH '''BYTE''';
'''RETURN''' ( CH &gt;= &apos;A&apos; '''AND''' CH &lt;= &apos;Z&apos; ) '''OR''' CH = &apos;$&apos;
'''OR''' ( CH &gt;= &apos;0&apos; '''AND''' CH &lt;= &apos;9&apos; );
'''END''' IS$WORD$CHAR ;
'''IF''' '''NOT''' FL$EXISTS( FCB$IN ) '''THEN''' '''DO''';
'''CALL''' SHOW$FCB( FCB$IN );
'''CALL''' PR$STRING( .&apos;: INPUT FILE NOT FOUND$&apos; );'''CALL''' PR$NL;
'''END''';
'''ELSE''' '''IF''' FL$EXISTS( FCB$OUT ) '''THEN''' '''DO''';
'''CALL''' SHOW$FCB( FCB$OUT );
'''CALL''' PR$STRING( .&apos;: OUTPUT FILE ALREADY EXISTS$&apos; );'''CALL''' PR$NL;
'''END''';
'''ELSE''' '''IF''' '''NOT''' FL$OPEN( FCB$IN ) '''THEN''' '''DO''';
'''CALL''' PR$STRING( .&apos;UNABLE TO OPEN THE INPUT FILE$&apos; );'''CALL''' PR$NL;
'''END''';
'''ELSE''' '''IF''' '''NOT''' FL$MAKE( FCB$OUT ) '''THEN''' '''DO''';
'''CALL''' PR$STRING( .&apos;UNABLE TO OPEN THE OUTPUT FILE$&apos; );'''CALL''' PR$NL;
'''IF''' '''NOT''' FL$CLOSE( FCB$IN ) '''THEN''' '''DO''';
'''CALL''' PR$STRING( .&apos;UNABLE TO CLOSE THE INPUT FILE$&apos; ); '''CALL''' PR$NL;
'''END''';
'''END''';
'''ELSE''' '''DO'''; ''/* FILES OPENED OK - ATTEMPT TO FORMAT THE SOURCE */''
'''DECLARE''' ( GOT$RCD, IS$HEADING ) '''BYTE''', ( DMA$END, OUT$END ) '''ADDRESS''';
'''DECLARE''' IN$STRING '''BYTE''', COMMENT$STATE '''ADDRESS''', GOT$NEXT '''BYTE''';
IN$CHAR: '''PROCEDURE''';
F$PTR = F$PTR + 1;
'''IF''' F$PTR &gt; DMA$END '''THEN''' '''DO'''; ''/* END OF BUFFER */''
GOT$RCD = FL$READ( FCB$IN ); ''/* GET THE NEXT RECORDD */''
'''IF''' '''NOT''' GOT$RCD '''THEN''' F$CHAR = EOF$CHAR;
F$PTR = DMA$BUFFER;
'''END''';
'''END''' IN$CHAR ;
OUT$CHAR: '''PROCEDURE'''( CH ); ''/* OUTPUT A CHARECTER TO THE OUTPOUT FILE */''
'''DECLARE''' CH '''BYTE''';
'''IF''' CH &lt;&gt; EOF$CHAR '''THEN''' '''CALL''' PR$CHAR( CH );
W$CHAR = CH;
W$PTR = W$PTR + 1;
'''IF''' W$PTR &gt; OUT$END '''OR''' CH = EOF$CHAR '''THEN''' '''DO''';
''/* THE OUTPUT BUFFER IS FULL OR WE ARE WRITTING EOF */''
'''IF''' CH = EOF$CHAR '''THEN''' '''DO'''; ''/* EOF - FILL THE BUFFER WITH NULS */''
'''DO''' '''WHILE''' W$PTR &lt;= OUT$END;
W$CHAR = 0;
W$PTR = W$PTR + 1;
'''END''';
'''END''';
'''CALL''' DMA$SET( OUT$BUFFER ); ''/* SWITCH DMA TO THE OUTOUT BUFFER */''
'''IF''' '''NOT''' FL$WRITE( FCB$OUT ) '''THEN''' '''DO'''; ''/* I/O ERROR */''
'''CALL''' PR$STRING( .&apos;I/O ERROR ON WRITING $&apos; );
'''CALL''' SHOW$FCB( FCB$OUT );
'''CALL''' PR$NL;
'''CALL''' EXIT;
'''END''';
'''CALL''' DMA$SET( DMA$BUFFER ); ''/* RESET DMA TO THE DEFAULT BUFFER */''
W$PTR = OUT$BUFFER;
'''END''';
'''END''' OUT$CHAR;
OUT$STRING: '''PROCEDURE'''( STR ); ''/* OUTPUT A STRING */''
'''DECLARE''' STR '''ADDRESS''';
'''DECLARE''' S$PTR '''ADDRESS''';
'''DECLARE''' S$CHAR '''BASED''' S$PTR '''BYTE''';
S$PTR = STR;
'''DO''' '''WHILE''' S$CHAR &lt;&gt; &apos;$&apos;;
'''CALL''' OUT$CHAR( S$CHAR );
S$PTR = S$PTR + 1;
'''END''';
'''END''' OUT$STRING;
DMA$END = DMA$BUFFER + ( DMA$SIZE - 1 );
OUT$END = OUT$BUFFER + ( DMA$SIZE - 1 );
GOT$RCD = FL$READ( FCB$IN ); ''/* GET THE FIRST RECORD */''
F$PTR = DMA$BUFFER;
W$PTR = OUT$BUFFER;
IN$STRING = FALSE;
GOT$NEXT = FALSE;
COMMENT$STATE = 0;
'''CALL''' OUT$CHAR( &apos; &apos; );
'''DO''' '''WHILE''' GOT$RCD;
'''IF''' F$CHAR = CR$CHAR '''THEN''' '''DO'''; ''/* CARRIAGE RETURN */''
'''IF''' COMMENT$STATE &gt; 1 '''THEN''' '''DO''';
COMMENT$STATE = 2;
'''CALL''' OUT$STRING( .&apos;&apos;&apos;&apos;&apos;$&apos; );
'''END''';
'''CALL''' OUT$CHAR( F$CHAR );
'''END''';
'''ELSE''' '''IF''' F$CHAR = NL$CHAR '''THEN''' '''DO''';
'''CALL''' OUT$CHAR( F$CHAR );
'''CALL''' OUT$CHAR( &apos; &apos; );
'''IF''' COMMENT$STATE &gt; 1 '''THEN''' '''CALL''' OUT$STRING( .&apos;&apos;&apos;&apos;&apos;$&apos; );
'''END''';
'''ELSE''' '''IF''' F$CHAR = AMP '''THEN''' '''DO''';
'''CALL''' OUT$STRING( .( AMP, LCA, LCM, LCP, &apos;;$&apos; ) );
'''END''';
'''ELSE''' '''IF''' F$CHAR = &apos;&apos;&apos;&apos; '''THEN''' '''DO''';
'''CALL''' OUT$STRING( .( AMP, LCA, LCP, LCO, LCS, &apos;;$&apos; ) );
IN$STRING = COMMENT$STATE = 0 '''AND''' '''NOT''' IN$STRING;
'''END''';
'''ELSE''' '''IF''' F$CHAR = &apos;&lt;&apos; '''THEN''' '''DO''';
'''CALL''' OUT$STRING( .( AMP, LCL, LCT, &apos;;$&apos; ) );
'''END''';
'''ELSE''' '''IF''' F$CHAR = &apos;&gt;&apos; '''THEN''' '''DO''';
'''CALL''' OUT$STRING( .( AMP, LCG, LCT, &apos;;$&apos; ) );
'''END''';
'''ELSE''' '''IF''' IN$STRING '''THEN''' '''CALL''' OUT$CHAR( F$CHAR );
'''ELSE''' '''IF''' COMMENT$STATE = 1 '''THEN''' '''DO'''; ''/* HAVE A CHARACTER AFTER / */''
'''IF''' F$CHAR = &apos;*&apos; '''THEN''' '''DO''';
COMMENT$STATE = 2;
'''CALL''' OUT$STRING( .&apos;&apos;&apos;&apos;&apos;/*$&apos; );
'''END''';
'''ELSE''' '''DO''';
COMMENT$STATE = 0;
'''CALL''' OUT$CHAR( &apos;/&apos; );
'''CALL''' OUT$CHAR( F$CHAR );
'''END''';
'''END''';
'''ELSE''' '''IF''' COMMENT$STATE = 2 '''THEN''' '''DO'''; ''/* IN A COMMENT */''
'''IF''' F$CHAR = &apos;*&apos; '''THEN''' COMMENT$STATE = 3;
'''CALL''' OUT$CHAR( F$CHAR );
'''END''';
'''ELSE''' '''IF''' COMMENT$STATE = 3 '''THEN''' '''DO'''; ''/* IN A COMMENT, EXPECTING / */''
'''IF''' F$CHAR = &apos;/&apos; '''THEN''' '''DO'''; ''/* END OF COMMENT */''
'''CALL''' OUT$STRING( .&apos;/&apos;&apos;&apos;&apos;$&apos; );
COMMENT$STATE = 0;
'''END''';
'''ELSE''' '''DO'''; ''/* NOT END OF COMMENT */''
'''CALL''' OUT$CHAR( F$CHAR );
'''IF''' F$CHAR &lt;&gt; &apos;*&apos; '''THEN''' COMMENT$STATE = 2;
'''END''';
'''END''';
'''ELSE''' '''IF''' F$CHAR = &apos;/&apos; '''THEN''' '''DO''';
'''IF''' COMMENT$STATE = 0 '''THEN''' COMMENT$STATE = 1;
'''ELSE''' '''IF''' COMMENT$STATE = 3 '''THEN''' '''DO''';
''/* END OF COMMENT */''
'''CALL''' OUT$STRING( .&apos;/&apos;&apos;&apos;&apos;$&apos; );
COMMENT$STATE = 0;
'''END''';
'''ELSE''' '''CALL''' OUT$CHAR( F$CHAR );
'''END''';
'''ELSE''' '''IF''' F$CHAR = EOF$CHAR '''THEN''' GOT$RCD = FALSE; ''/* END OF FILE */''
'''ELSE''' '''IF''' F$CHAR &gt;= &apos;A&apos; '''AND''' F$CHAR &lt;= &apos;Z&apos; '''THEN''' '''DO'''; ''/* WORD */''
'''DECLARE''' W ( 10 )'''BYTE''', W$POS '''BYTE''', HAS$DOLLAR '''BYTE''';
OUT$WORD: '''PROCEDURE'''; ''/* OUTPUT W (WHICH MAY CONTAIN $ */''
'''DECLARE''' I '''BYTE''';
'''DO''' I = 0 '''TO''' W$POS - 1; '''CALL''' OUT$CHAR( W( I ) ); '''END''';
'''END''' OUT$WORD ;
W$POS = 0;
HAS$DOLLAR = FALSE;
'''DO''' '''WHILE''' W$POS &lt; 9 '''AND''' IS$WORD$CHAR( F$CHAR );
'''IF''' F$CHAR = &apos;$&apos; '''THEN''' HAS$DOLLAR = TRUE;
W( W$POS ) = F$CHAR; W$POS = W$POS + 1; '''CALL''' IN$CHAR;
'''END''';
W( W$POS ) = &apos;$&apos;;
'''IF''' IS$WORD$CHAR( F$CHAR ) '''THEN''' '''DO'''; ''/* WORD IS TOO LONG FOE A */''
'''CALL''' OUT$WORD; ''/* KEYWORD */''
'''DO''' '''WHILE''' IS$WORD$CHAR( F$CHAR );
'''CALL''' OUT$CHAR( F$CHAR );'''CALL''' IN$CHAR;
'''END''';
'''END''';
'''ELSE''' '''IF''' HAS$DOLLAR '''THEN''' '''DO'''; ''/* ASSUME IT ISN&apos;T A KEYWORD */''
'''CALL''' OUT$WORD; ''/* I.E., THE PROGRAMMER HASN&apos;T WRITTEN E.G.: */''
'''END'''; ''/* RE$TURN X; */''
'''ELSE''' '''DO'''; ''/* SHORT WORD - COULD BE A KEYWORD */''
'''DECLARE''' ( IS$KW, KW$POS ) '''BYTE''';
IS$KW = FALSE;
KW$POS = 0;
'''DO''' '''WHILE''' '''NOT''' IS$KW '''AND''' KW$POS &lt;= KW$MAX;
IS$KW = STR$EQUAL( .W, KW( KW$POS ) );
KW$POS = KW$POS + 1;
'''END''';
'''IF''' IS$KW '''THEN''' '''CALL''' OUT$STRING( .&apos;&apos;&apos;&apos;&apos;&apos;&apos;$&apos; );
'''CALL''' OUT$WORD;
'''IF''' IS$KW '''THEN''' '''CALL''' OUT$STRING( .&apos;&apos;&apos;&apos;&apos;&apos;&apos;$&apos; );
'''END''';
GOT$NEXT = TRUE;
'''END''';
'''ELSE''' '''DO'''; ''/* HAVE ANOTHER CHARACTER */''
'''CALL''' OUT$CHAR( F$CHAR );
'''END''';
'''IF''' '''NOT''' GOT$NEXT '''THEN''' '''CALL''' IN$CHAR;
GOT$NEXT = FALSE;
'''END''';
'''CALL''' OUT$CHAR( EOF$CHAR );
''/* CLOSE THE FILES */''
'''IF''' '''NOT''' FL$CLOSE( FCB$IN ) '''THEN''' '''DO''';
'''CALL''' PR$STRING( .&apos;UNABLE TO CLOSE THE INPUT FILE$&apos; ); '''CALL''' PR$NL;
'''END''';
'''IF''' '''NOT''' FL$CLOSE( FCB$OUT ) '''THEN''' '''DO''';
'''CALL''' PR$STRING( .&apos;UNABLE TO CLOSE THE OUTPUT FILE$&apos; ); '''CALL''' PR$NL;
'''END''';
'''END''';
'''CALL''' EXIT;
'''EOF'''


=={{header|Python}}==
=={{header|Python}}==
Line 678: Line 1,139:
'''if''' __name__ == &quot;__main__&quot;:
'''if''' __name__ == &quot;__main__&quot;:
main()
main()

=={{header|Wren}}==
{{libheader|Wren-ioutil}}
Note that, rightly or wrongly, this code would not highlight keywords occurring in interpolated string expressions.

''// Convert a Wren source to "wiki" format:''
''// each line is preceded by a space''
''// keywords are enclosed in &apos;&apos;&apos; and &apos;&apos;&apos; and comments in &apos;&apos; and &apos;&apos;''
''// &apos;, &amp;, &lt; and &gt; are converted to &amp;apos; &amp;amp; &amp;lt; and &amp;gt;''
''// everything else is output as is''
''// The source is read from a file and written to standard output.''
''// The file name should be passed as a command line argument.''
'''import''' "./ioutil" '''for''' FileUtil
'''import''' "os" '''for''' Process
'''var''' keywords = [
"as", "break", "class", "construct", "continue", "else", "false",
"for", "foreign", "if", "in", "is", "import", "null", "return",
"static", "super", "this", "true", "var", "while"
]
'''var''' alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_."
'''var''' highlight = Fn.new { |lines|
'''var''' inStr = '''false''' ''// within a string literal''
'''var''' inRaw = '''false''' ''// within a raw string literal''
'''var''' inCom = '''false''' ''// within a multi-line comment''
'''var''' level = 0 ''// nesting level for multi-line comment''
'''for''' (line '''in''' lines) {
System.write(" ")
line = line.replace("&amp;", "&amp;amp;").replace("&apos;", "&amp;apos;")
.replace("&lt;", "&amp;lt;").replace("&gt;", "&amp;gt;")
'''var''' word = ""
'''var''' chrs = line.toList ''// convert to list of unicode characters''
'''var''' cc = chrs.count
'''var''' i = 0
'''if''' (inCom) System.write("&apos;&apos;")
'''while''' (i &lt; cc) {
'''var''' c = chrs[i]
'''if''' (inCom) { ''// if inside a multi-line comment''
'''if''' (c == "/" &amp;&amp; i &lt; cc-1 &amp;&amp; chrs[i+1] == "*") {
level = level + 1
System.write("/*")
i = i + 1
} '''else''' '''if''' (c == "*" &amp;&amp; i &lt; cc-1 &amp;&amp; chrs[i+1] == "/") {
level = level - 1
System.write("*/")
i = i + 1
'''if''' (level == 0) {
inCom = '''false'''
System.write("&apos;&apos;")
}
} '''else''' {
System.write(c)
}
} '''else''' '''if''' (inStr &amp;&amp; c == "\\" &amp;&amp; i &lt; cc-1 &amp;&amp; chrs[i+1] == "\"") {
''/* escaped double quote in string literal */''
System.write("\\\"")
i = i + 1
} '''else''' '''if''' (c == "\"") { ''// any other double quote''
'''if''' (i &gt; 1 &amp;&amp; chrs[i-2] == "\"" &amp;&amp; chrs[i-1] == "\"") {
inRaw = !inRaw
} '''else''' '''if''' (!inRaw) {
inStr = !inStr
}
System.write("\"")
} '''else''' '''if''' (inStr || inRaw) { ''// otherwise if within a string just write it''
System.write(c)
} '''else''' '''if''' (c == "/") { ''// forward slash''
'''if''' (i &lt; cc-1 &amp;&amp; chrs[i+1] == c) {
System.write("&apos;&apos;" + chrs[i..-1].join() + "&apos;&apos;")
'''break'''
} '''else''' '''if''' (i &lt; cc-1 &amp;&amp; chrs[i+1] == "*") {
inCom = '''true'''
level = 1
System.write("&apos;&apos;" + "/*")
i = i + 1
} '''else''' {
System.write(c)
}
} '''else''' '''if''' (alphabet.contains(c)) { ''// if eligible, add to current word''
word = word + c
} '''else''' '''if''' (keywords.contains(word)) { ''// if it&apos;s a keyword, embolden it''
System.write("&apos;&apos;&apos;" + word + "&apos;&apos;&apos;" + c)
word = ""
} '''else''' { ''// otherwise just write the word''
System.write(word + c)
word = ""
}
i = i + 1
}
'''if''' (inCom) {
System.write("&apos;&apos;")
} '''else''' '''if''' (word != "") {
'''if''' (keywords.contains(word)) {
System.write("&apos;&apos;&apos;" + word + "&apos;&apos;&apos;")
} '''else''' {
System.write(word)
}
}
System.print()
}
}
'''var''' args = Process.arguments
'''if''' (args.count != 1) {
''/* make sure double quotes and keywords in raw strings are handled properly */''
Fiber.abort("""Please pass the file name to be highlighted "as" the only argument.""")
}
'''var''' lines = FileUtil.readLines(args[0])
highlight.call(lines)
''/* this code should now be saved to''
'' /* a file named */''
'' Syntax_highlighting_using_Mediawiki_formatting.wren */''

=={{header|XPL0}}==
Key words in XPL0 are easy to distinguish from variable names because
they start with lowercase letters while variables start with uppercase
letters. This program highlights its own code properly, but it will not
properly highlight all possible cases, such as when key words appear
in quoted strings.
'''proc''' CharOut(Ch);
'''int''' Ch;
'''begin'''
'''case''' Ch '''of'''
^&apos;: Text(0, "&amp;apos;");
^&amp;: Text(0, "&amp;amp;");
^&lt;: Text(0, "&amp;lt;");
^&gt;: Text(0, "&amp;gt;")
'''other''' ChOut(1, Ch);
'''end''';
'''int''' Ch;
'''loop''' '''begin'''
ChOut(1, $20); ''\leading space''
Ch:= ChIn(1);
'''loop''' '''begin'''
'''while''' Ch &lt;= $20 '''do''' ''\pass whitespace to output''
'''begin'''
'''if''' Ch = $1A ''\EOF\'' '''then''' '''return''';
ChOut(1, Ch);
'''if''' Ch = $0A ''\LF\'' '''then''' '''quit''';
Ch:= ChIn(1);
'''end''';
'''if''' Ch = ^\ '''then''' ''\pass comment to output''
'''begin'''
Text(0, "&apos;&apos;"); ''\in italics''
ChOut(1, Ch);
Ch:= ChIn(1);
'''while''' Ch#^\ &amp; Ch#$0A ''\LF\'' '''do'''
'''begin'''
CharOut(Ch); Ch:= ChIn(1);
'''end''';
'''if''' Ch = ^\ '''then''' ChOut(1, Ch);
Text(0, "&apos;&apos;");
'''if''' Ch = $0A ''\LF\'' '''then'''
'''begin'''
ChOut(1, Ch); '''quit''';
'''end''';
Ch:= ChIn(1);
'''end'''
'''else''' '''if''' Ch&gt;=^a &amp; Ch&lt;=^z '''then''' ''\pass key words to output''
'''begin'''
Text(0, "&apos;&apos;&apos;"); ''\in bold''
'''while''' Ch&gt;=^a &amp; Ch&lt;=^z '''do'''
'''begin'''
ChOut(1, Ch); Ch:= ChIn(1);
'''end''';
Text(0, "&apos;&apos;&apos;");
'''end'''
'''else''' '''begin''' ''\pass anything else''
'''repeat''' CharOut(Ch);
Ch:= ChIn(1);
'''until''' Ch &lt;= $20; ''\until whitespace''
'''end''';
'''end''';
'''end'''

Revision as of 11:27, 11 February 2024

Syntax highlighting using Mediawiki formatting is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Introduction

When formatting a page for display, Mediawiki allows the page to include bold and italic text by placing the bold/italic text within paired repeated-single quote characters - 3 single quotes for bold and 2 for italic, 5 for bold italic.
E.g.: '''bold-word''' and ''italic-word'' appears as bold-word and italic-word.

This could be used to provide simple syntax-highlighting without the use of the relatively more expensive <syntaxhighlight> tags or for languages not currently supported by Pygments. A few languages on Rosetta Code are currently using schemes like this.

Task

The task is to write a syntax highlighter that given a source in your language will output a wiki formatted version of the source with the keywords/reserved words in bold and the comments in italics.
Note that each source line (including blank lines) should be output with a leading space, to ensure the source is treated as a single block.

Additionally, translate the following characters:

  • single-quote (') to &apos;
  • ampersand (&) to &amp;
  • less-than (<) to &lt;
  • greater-than (>) to &gt;

If your language doesn't have keywords/reserved words or comments, use your judgement on what to highlight in bold or italic : )

Presenting your source

Instead of showing your source within syntaxhighlight tags and having a separate output block, just show the source that would be output from your program when given its own source to process.
I.e., don't use syntaxhighlight tags.

See also

https://www.mediawiki.org/wiki/Help:Formatting

ALGOL 68

Handles upper-stropping Algol 68 sources (as used by ALGOL 68G and most other compilers).

CO Convert an upper-stropped Algol 68 source to "wiki" format
     each line is preceeded by a space,
     bold words are enclosed in ''' and ''' and comments in '' and ''
     ', &, < and > are converted to &apos; &amp; &lt; and &gt;
     everything else if output as is
     quote-stropping, point-stropping and res-stropping is not suppoered
  the source is read from stand in and written to stand out
  the last line in the file must end with a newline
  { and } are assumed to be alternatives for ( and ), if { } should be
      treated as comments ( as in ALGOL68RS/algol68toc )
          change rs style brief comments to TRUE
CO
BEGIN

    BOOL   in string         := FALSE;
    BOOL   in brief comment  := FALSE;
    INT    rs comment depth  := 0;
    STRING comment delimiter := "";

    # TRUE if {} delimits a nestable brief comment, as in ALGOL 68RS and      #
    #      algol68toc, FALSE if {} are alternatives to () as in ALGOL 68G     #
    BOOL rs style brief comments = FALSE;

    BOOL at eof := FALSE;     # TRUE if EOF has been reached, FALSE otherwise #
    on logical file end( stand in              # set EOF handler for stand in #
                       , ( REF FILE f )BOOL:
                             # note that we reached EOF on the latest read    #
                             # and return TRUE so processing can continue     #
                             at eof := TRUE
                       );
    CHAR   nl        = REPR 10;                           # newline character #
    STRING line     := nl;                              # current source line #
    INT    pos      := LWB line;                   # current position in line #
    CHAR   c        := " ";                        # current source character #

    PROC next char = VOID:   # gets the next source character, stores it in c #
         IF pos <= UPB line THEN
             c := line[ pos ];          # not past the end of the source line #
             pos +:= 1
         ELIF        # past the end of the current source line - get the next #
             at eof := FALSE;
             read( ( line, newline ) );
             NOT at eof
         THEN
             line +:= nl;                                 # have another line #
             c     := line[ pos := LWB line ];
             pos  +:= 1
         ELSE
             line := "";                                        # reached eof #
             c    := REPR 0
         FI # next char # ;
    PROC out char = ( CHAR ch )VOID:               # conveerts and outputs ch #
         IF   ch = nl  THEN
             IF NOT in brief comment AND rs comment depth = 0 AND comment delimiter = "" THEN
                 print( ( newline, " " ) )         # newline not in a comment #
             ELSE                                      # newline in a comment #
                 italic delimiter; print( ( newline, " " ) ); italic delimiter
             FI
         ELIF ch = "<" THEN print( ( "&lt;" ) )
         ELIF ch = ">" THEN print( ( "&gt;" ) )
         ELIF ch = "&" THEN print( ( "&amp;" ) )
         ELIF ch = "'" THEN print( ( "&apos;" ) )
         ELSE print( ch )
         FI # out char # ;
    # outputs the current character and gets the next                         #
    PROC out and next char = VOID: BEGIN out char( c ); next char END;
    # outputs a wiki start/end italic delimiter                               #
    PROC italic delimiter = VOID: print( ( "''" ) );
    # returns TRUE if the current character can start a bold word             #
    PROC have bold = BOOL: c >= "A" AND c <= "Z";
    PROC get bold word = STRING:          # gets a bold word from then source #
         BEGIN
            STRING result := "";
            WHILE have bold OR c = "_" DO result +:= c; next char OD;
            result
         END # get bold word # ;

    # copy the source to stand out, conveerting to wiki format                #
    next char;
    WHILE NOT at eof DO
        IF   in string THEN                           # currently in a string #
            in string := c /="""";
            out and next char
        ELIF in brief comment THEN             # currently in a brief comment #
            in brief comment := c /= "#";
            out and next char;
            IF NOT in brief comment THEN italic delimiter FI
        ELIF rs comment depth > 0 THEN # currently in a nesting {...} comment #
            IF c = "}" THEN rs comment depth -:= 1 FI;
            out and next char;
            IF rs comment depth < 1 THEN italic delimiter FI
        ELIF comment delimiter /= "" THEN           # in a CO/COMMENT comment #
            IF NOT have bold THEN
                out and next char               # haven't reached a bold word #
            ELSE
                STRING word = get bold word;    # at the start of a bold word #
                print( ( word ) );
                IF word = comment delimiter THEN
                    # reached the end of the comment                          #
                    italic delimiter;
                    comment delimiter := ""
                FI
            FI
        ELIF c = """" THEN        # start of a string or character denotation #
            out and next char;
            in string := TRUE
        ELIF c = "#" THEN         # start of a brief comment such as this one #
            italic delimiter;
            out and next char;
            in brief comment := TRUE
        ELIF c = "{" AND rs style brief comments THEN        # nestable brief #
            italic delimiter;         # comment ( ALGOL 68RS and algol68toc ) #
            out and next char;
            rs comment depth := 1
        ELIF have bold THEN                                # have a bold word #
            STRING word = get bold word;
            IF word /= "CO" AND word /= "COMMENT" THEN
                print( ( "'''", word, "'''" ) )       # non-comment bold word #
            ELSE
                italic delimiter;                   # start of a bold comment #
                print( ( word ) );
                comment delimiter := word
            FI
        ELSE                                                  # anything else #
            out and next char
        FI
    OD;
    IF   in string               THEN print( ( "**** unterminated string",              newline ) )
    ELIF in brief comment        THEN print( ( "**** unterminated brief comment",       newline ) )
    ELIF rs comment depth > 0    THEN print( ( "**** unterminated {...} comment",       newline ) )
    ELIF comment delimiter /= "" THEN print( ( "**** unterminated ", comment delimiter, newline ) )
    FI

END

ALGOL W

begin comment syntax highlight an Algol W source using Mediawiki formatting
              the source is read from standard input and written to standard output
            ;
    % Algol W strings are limited to 256 characters in length so source lines %
    % are limited to 256 characters %
 
    integer     lineWidth, errorCount, lowerA, upperA, linePos, kwMax;
    integer     MAX_TOKEN_LENGTH;
    string(1)   nl;
    string(256) line;
    string(1)   currChar, commentEnd;
    string(9)   array kw ( 1 :: 64 );
    logical     inString, inComment;
 
    % returns true if currChar is in the inclusive range low to high, false otherwise %
    logical procedure range( string(1) value low, high ) ; currChar >= low and currChar <= high;
    procedure nextChar ;               % gets the next source character %
        if      linePos = lineWidth then begin
            currChar := nl;
            linePos  := linePos + 1
            end
        else if linePos > lineWidth then begin
            readcard( line );
            lineWidth := 256;
            while lineWidth > 1 and line( lineWidth - 1 // 1 ) = " " do lineWidth := lineWidth - 1;
            linePos   := 1;
            currChar  := line( 0 // 1 )
            end
        else begin
            currChar := line( linePos // 1 );
            linePos  := linePos + 1
        end nextChar ;
    % returns true if the current character can start an identifier, false otherwise %
    logical procedure identifierStartChar ; range( "a", "z" ) or range( "A", "Z" );
    % returns true if the current character can be pat of an identifier, false otherwise %
    logical procedure identifierChar ; identifierStartChar or range( "0", "9" ) or currChar = "_";
    procedure outAndNextChar ; begin % output currChar and get the next %
        if      currChar = "'" then writeon( "&apos;" )
        else if currChar = "&" then writeon( "&amp;"  )
        else if currChar = "<" then writeon( "&lt;"   )
        else if currChar = ">" then writeon( "&gt;"   )
        else if currChar = nl  then begin
            if inComment then writeon( "''" );
            write( " " );
            if inComment then writeon( "''" )
            end
        else writeon( currChar );
        nextChar
    end outAndNextChar ;
    procedure identifierOrKeyword ; begin % handle an indentifier or keyword %
        string(9) word, lWord;
        integer   wLength;
        % recursive keyword binary search %
        logical procedure isKeyword ( integer value low, high ) ;
            if high < low then false
            else begin
                integer mid;
                mid := ( low + high ) div 2;
                if      kw( mid ) > lWord then isKeyword( low,     mid - 1 )
                else if kw( mid ) = lWord then true
                else                           isKeyword( mid + 1, high    )
            end binarySearchR ;
        wLength := 0;
        for chPos := 0 until 8 do begin
            if identifierChar then begin
                word(  chPos // 1 ) := currChar;
                lWord( chPos // 1 ) := if range( "A", "Z" ) then code( ( decode( currChar ) - upperA ) + lowerA )
                                                            else currChar;;
                wLength := wLength + 1;
                nextChar
                end
            else begin
                lWord( chPos // 1 ) := " ";
                word(  chPos // 1 ) := " "
            end if_identifierChar__
        end for_chPos ;
        if identifierChar then begin
             % all keywords are <= 9 characters long so this must be an identifier %
             writeon( word );
             while identifierChar do outAndNextChar
             end
        else if lWord = "comment" then begin
             writeon( "''comment" );
             commentEnd := ";";
             inComment  := true
             end
        else if isKeyword( 1, kwMax ) then begin
             writeon( "'''" );
             for chPos := 0 until wLength - 1 do writeon( word( chPos // 1 ) );
             writeon( "'''" )
             end
        else begin % identifier %
             for chPos := 0 until wLength - 1 do writeon( word( chPos // 1 ) )
        end if_various_words
    end identifierOrKeyword ;
 
    s_w := 0; i_w := 1; % output formarting %
 
    MAX_TOKEN_LENGTH := 256;
    nl               := code( 10 );
    lowerA           := decode( "a" );
    upperA           := decode( "A" );
 
    % allow the program to continue after reaching end-of-file %
    ENDFILE := EXCEPTION( false, 1, 0, false, "EOF" );
    % ensure the first call to nextChar reads the first line %
    lineWidth := 256;
    linePos   := lineWidth + 1;
    currChar  := " ";
 
    begin    % keywords %
        procedure K ( string(9) value kwStr ) ; begin
            kwMax := kwMax + 1;
            kw( kwMax ) := kwStr
        end K ;
        kwMax := 0;
        K("abs");K("algol");K("and");K("array");K("assert");K("begin");K("bits");
        K("case");K("complex");K("div");K("do");K("else");K("end");K("false");
        K("for");K("fortran");K("go");K("goto");K("if");K("integer");K("is");
        K("logical");K("long");K("not");K("null");K("of");K("or");
        K("procedure");K("real");K("record");K("reference");K("rem");K("result");
        K("shl");K("short");K("shr");K("step");K("string");
        K("then");K("to");K("true");K("until");K("value");K("while")
    end keywords ;
 
    inString := inComment := false;
    outAndNextChar;
    while not XCPNOTED(ENDFILE) do begin
        if      inString            then begin % in a string %
            inString := currChar not = """";
            outAndNextChar;
            end
        else if inComment           then begin % in a comment %
            inComment := currChar not = ";" and currChar not = commentEnd;
            outAndNextChar;
            if not inComment then writeon( "''" );
            end
        else if identifierStartChar then identifierOrKeyword
        else if currChar = """"     then begin % string literal %
            outAndNextChar;
            inString := true
            end
        else if currChar = "%"      then begin % brief comment %
            writeon( "''" );
            commentEnd := "%";
            inComment  := true;
            outAndNextChar
            end
        else outAndNextChar
    end while_not_ar_eof ;
 
    if      inComment then write( "**** unterminated comment" )
    else if inString  then write( "**** unterminated string"  )
 
end.

AWK

Parsing of patterns may not be correct in all cases.

# convert an AWK source to wiki format
#    each line is preceeded by a space,
#    reserved words are enclosed in ''' and ''' and comments in '' and ''
#    ', &, < and > are converted to &apos; &amp; &lt; and &gt;
#    everything else if output as is
# the wiki source is written to stdout

BEGIN \
{
    # reserved word list as in gawk and treating getline as reserved 
    kw = "BEGIN/BEGINFILE/END/ENDFILE/"                         \
         "break/case/continue/default/delete/do/while/else/"    \
         "exit/for/in/function/func/if/next/nextfile/switch/"   \
         "getline";
    n   = split( kw, reservedWords, "/" );
    for( w = 1; w <= n; w ++ )
    {
        reserved[ reservedWords[ w ] ] = w;
    }

} # BEGIN


{
    printf( " " );
    line = $0;
    gsub( /&/, "\\&amp;",  line );
    gsub( /</, "\\&lt;",   line );
    gsub( />/, "\\&gt;",   line );
    gsub( /'/, "\\&apos;", line );

    if( line != "" )
    {
        c = "";
        nextChar();
        do
        {
            if     ( c == "#" )
            {
                # comment
                printf( "''#%s''", line );
                c = "";
            }
            else if( c == "\"" )
            {
                # string literal
                do
                {
                    if( c == "\\" )
                    {
                        outAndNextChar();
                    }
                    outAndNextChar();
                }
                while( c != "\"" && c != "" );
                if( c != "\"" )
                {
                    printf( "**** Unterminated string\n" );
                }
                else
                {
                    nextChar();
                }
                printf( "\"" );
            }
            else if( c == "/" && lastC !~ /[A-Za-z0-9_.]/ )
            {
                # pattern
                bracketDepth = 0;
                outAndNextChar();
                while( c != "" && ( c != "/" || bracketDepth > 0 ) )
                {
                    if( c == "\\" || c == "[" )
                    {
                        if( c == "[" )
                        {
                            bracketDepth ++;
                        }
                        outAndNextChar();
                    }
                    else if( c == "]" )
                    {
                        bracketDepth --;
                    }
                    outAndNextChar();
                }
                if( c != "/" )
                {
                    printf( "**** Unterminated pattern\n" );
                }
                else
                {
                    nextChar();
                }
                printf( "/" );
            }
            else if( c ~ /[A-Za-z]/ )
            {
                # have a reserved word or identifier
                word = "";
                do
                {
                    word = word c;
                    nextChar();
                }
                while( c ~ /[A-Za-z0-9_]/ );
                if( word in reserved )
                {
                    word = "'''" word "'''";
                }
                printf( "%s", word );
            }
            else
            {
                # something else
                outAndNextChar();
            }
        }
        while( c != "" );
    }
    printf( "\n" );

}

function outAndNextChar()
{
    printf( "%s", c );
    nextChar();
}

function nextChar()
{
    if( c != " " )
    {
        # the last character wasn't a space, save it so we can recognise patterns
        lastC = c;
    }
    if( line == "" )
    {
        # at end of line
        lastC = c = "";
    }
    else
    {
        # not end of line
        c    = substr( line, 1, 1 );
        line = substr( line, 2 );
    }

} # nextChar

Julia

#= Keywords in Julia. Handles two word reserved keywords. #= Also
   #= handles nested comments such as this. =# =#
=#
const KEYWORDS = map(
    w -> Regex("^" * w * "\\W"),
    sort(
        [
            raw"abstract\s+type",
            "baremodule",
            "begin",
            "break",
            "catch",
            "const",
            "continue",
            "do",
            "else",
            "elseif",
            "end",
            "export",
            "false",
            "finally",
            "for",
            "function",
            "global",
            "if",
            "import",
            "in",
            "isa",
            "let",
            "local",
            "macro",
            "module",
            raw"mutable\s+struct",
            "outer",
            raw"primitive\s+type",
            "quote",
            "return",
            "struct",
            "true",
            "try",
            "using",
            "while",
            "where",
        ], rev = true, by = length),
) # reorder to largest first then convert to Regex

""" Find the #= =# delineated comment, including nested versions """
function nestedcommentlimits(s::AbstractString, startcomment = "#=", stopcomment = "=#")
    either = Regex("$startcomment|$stopcomment", "sa")
    depth, startpos, stoppos = 0, 0, 0
    for m in eachmatch(either, s)
        if m.match == startcomment
            startpos = startpos == 0 ? m.match.offset : startpos
            depth += 1
        else
            stoppos = max(stoppos + 1, m.match.offset + 2)
            depth -= 1
        end
        depth <= 0 && break
    end
    return startpos, stoppos
end

"""
   Given a string, output a string that has been modified by adding surrounding
   \'\' or \'\'\' bracketing for syntax highlighting of keywords and comments
"""
function partialhighlight(txt)
    outtxt = Char[]
    idx, len = 1, length(txt)
    while idx <= len
        if !isvalid(txt, idx) # exclude internal positions of multibyte Char
            idx += 1
            continue
        end
        c = txt[idx]
        if c == '\\' # escape the next char, send as is
            push!(outtxt, c, txt[idx+1])
            idx += 2
        elseif c == '\"' # quotation start
            if idx < len - 2 && c == txt[idx+1] == txt[idx+2] # """ quotes """
                qlen = findfirst(r"(?<!\\)\"\"\""sa, txt[idx+3:end])
                qlen == nothing && error("error with terminator of quote at $idx")
                append!(outtxt, collect(replace(txt[idx:idx+qlen.stop+2], "\n" => "\n ")))
                idx += qlen.stop + 3
            else # " quote "
                qlen = findfirst(r"(?<!\\)\"", txt[idx+1:end])
                qlen == nothing && error("error with terminator of quote at $idx")
                append!(outtxt, collect(replace(txt[idx:idx+qlen.stop+1], "\n" => "\n ")))
                idx += qlen.stop + 2
            end
        elseif c == '#' && txt[max(1, idx - 1)] != ''' # start comment
            if idx < len && txt[idx+1] == '=' #= comment =#
                start, stop = nestedcommentlimits(txt[idx:end])
                s = replace(txt[idx:idx+stop-1], "\n" => "\n ")
                append!(outtxt, collect("\'\'$s\'\'"))
                idx += stop
            else # found a line comment, like this comment
                newlinepos = something(findfirst(==('\n'), txt[idx+1:end]), len - idx)
                append!(outtxt, collect("\'\'$(txt[idx:idx+newlinepos-1])\'\'"))
                idx += newlinepos
            end
        elseif c ∈ 'a':'z' # lowercase char so check for keyword match
            for (j, reg) in enumerate(KEYWORDS)
                m = match(reg, txt[idx:end])
                if m != nothing
                    wlen = m.match.ncodeunits - 2
                    append!(outtxt, collect("\'\'\'$(txt[idx:idx+wlen])\'\'\'"))
                    idx += wlen + 1
                    break
                elseif j == lastindex(KEYWORDS) # no keyword found, send char to output
                    push!(outtxt, c)
                    idx += 1
                end
            end
        elseif c in [''', '&', '<', '>'] # \x26 is char & for HTML entity translation
            s = c == ''' ? "\x26apos;" : c == '&' ? "\x26amp;" : c == '<' ? "\x26lt;" : "\x26gt;"
            append!(outtxt, collect(s))
            idx += 1
        else # nothing special found, so pass char to output and increment index into input
            push!(outtxt, c)
            idx += 1
        end
        outtxt[end] == '\n' && push!(outtxt, ' ')
    end
    return String(outtxt)
end

println(partialhighlight(read(PROGRAM_FILE, String)))

Phix

Note the utility I use for this on a day-to-day basis (pwa/p2js.exw/<Ctrl M>) must be easily over 50,000 lines of code by now...
The following is deliberately the simplest possible thing that gets the job done, and there are of course 1,001 things missing: No support for [multiline] shebangs, C-style comments, nested block comments, or (as noted) Eu-compatible block comments; and keywords c/should easily be several hundred entries long, and tested/constructed using A-Z and 0-9, ...

--
-- demo\rosetta\syntax_highlight.exw
-- =================================
--
string pgm = substitute(get_text(command_line()[$]),"\r\n","\n")
-- or(/for javascript compatibility) specify constant pgm = """...""" 
constant qqq = `""`&`"`, /* (split to assist with permitting ^^^) */
         keywords = {`and`,`assert`,`bool`,`command_line`,`constant`,`do`,`else`,`elsif`,`end`,
        `find`,`for`,`function`,`get_text`,`if`,`iff`,`in`,`integer`,`length`,`match`,`not`,
        `procedure`,`puts`,`return`,`sequence`,`string`,`substitute`,`then`,`wait_key`,`while`},
         htmlify = {"'&<>",{`apos`,`amp`,`lt`,`gt`}}
integer i = 1, l = length(pgm), word_start = 0
string out = " "

procedure spacenl(sequence s)
    for ch in s do
        integer k = find(ch,htmlify[1])
        if k then ch = '&' & htmlify[2][k] & ';' end if
        out &= ch
        if ch='\n' then out &= ' ' end if
    end for
end procedure

function do_string(integer i, ni, l, string stype)
    assert(ni>0,"%d quoted string not closed",{stype})
    ni += l
    spacenl(pgm[i..ni])
    return ni
end function

while i<=l do
    integer ch = pgm[i]
    if (ch>='a' and ch<='z') or ch='_' then
        if not word_start then word_start := i end if
    else
        if word_start then
            string one_word = pgm[word_start..i-1]
            bool is_key = find(one_word,keywords)
            if is_key then out &= `'''` end if
            out &= one_word
            if is_key then out &= `'''` end if
            word_start = 0
        end if
        if ch='-' and i<l and pgm[i+1]='-' then
            -- nb: does not handle --/* style comments
            integer line_comment = i
            while i<l and pgm[i+1]!='\n' do i += 1 end while
            out &= `''`
            spacenl(pgm[line_comment..i])
            out &= `''`
        elsif ch='/' and i<l and pgm[i+1]='*' then
            -- nb: does not handle nested block comments
            integer block_comment = i
            i = match(`*/`,pgm,i+2)+1
            assert(i>1,"missing closing block comment")
            out &= `''`
            spacenl(pgm[block_comment..i])
            out &= `''`
        elsif ch='"' then
            if i+1<l and pgm[i..i+2]=qqq then
                i = do_string(i,match(qqq,pgm,i+3),2,"triple")
            else
                i = do_string(i,find('"',pgm,i+1),0,"double")
            end if
        elsif find(ch,"`'") then
            string stype = iff(ch='`'?"backtick":"single")
            i = do_string(i,find(ch,pgm,i+1),0,stype)
        else
            spacenl({ch})
        end if
    end if
    i += 1
end while
puts(1,out)
{} = wait_key()

PL/M

Works with: 8080 PL/M Compiler

... under CP/M (or an emulator)

Note that PL/M doesn't have in-built I/O or standard libraries, hence the need to define the various BDOS system calls.
As CP/M doesn't have redirection, the source file and output file names must be specified on the command line, e.g. if the source is in D:SYNTAX.PLM and the desired output file is D:SYNTAX.OUT and the program is compiled to D:SYNTAX.COM, then the command:
D:SYNTAX D:SYNTAX.PLM D:SYNTAX.OUT
will create SYNTAX.OUT as a copy of SYNTAX.PLM with the markup for the highlighting. Note the output file must not exist before running the program.
The output is also echoed to the console.

100H: /* SYNTAX HIGHLIGHT A PL/M SOURCE USING MEDIAWIKI MARKUP               */

   DECLARE FALSE    LITERALLY '0', TRUE LITERALLY '0FFH';
   DECLARE NL$CHAR  LITERALLY  '0AH';                    /* NEWLINE: CHAR 10 */
   DECLARE CR$CHAR  LITERALLY  '0DH';            /* CARRIAGE RETURN, CHAR 13 */
   DECLARE EOF$CHAR LITERALLY   '26';                         /* EOF: CTRL-Z */
   DECLARE AMP      LITERALLY '026H';                           /* AMPERSAND */
   DECLARE LCA      LITERALLY '061H';                      /* LOWER CASE 'A' */
   DECLARE LCG      LITERALLY '067H';                      /* LOWER CASE 'G' */
   DECLARE LCL      LITERALLY '06CH';                      /* LOWER CASE 'L' */
   DECLARE LCM      LITERALLY '06DH';                      /* LOWER CASE 'M' */
   DECLARE LCO      LITERALLY '06FH';                      /* LOWER CASE 'O' */
   DECLARE LCP      LITERALLY '070H';                      /* LOWER CASE 'P' */
   DECLARE LCS      LITERALLY '073H';                      /* LOWER CASE 'S' */
   DECLARE LCT      LITERALLY '074H';                      /* LOWER CASE 'T' */

   /* CP/M BDOS SYSTEM CALL, RETURNS A VALUE                                 */
   BDOS: PROCEDURE( FN, ARG )BYTE; DECLARE FN BYTE, ARG ADDRESS; GOTO 5; END;
   /* CP/M BDOS SYSTEM CALL, NO RETURN VALUE */
   BDOS$P: PROCEDURE( FN, ARG );   DECLARE FN BYTE, ARG ADDRESS; GOTO 5; END;
   EXIT:      PROCEDURE; CALL BDOS$P( 0, 0 ); END;      /* CP/M SYSTEM RESET */
   PR$CHAR:   PROCEDURE( C ); DECLARE C BYTE;    CALL BDOS$P( 2, C );    END;
   PR$STRING: PROCEDURE( S ); DECLARE S ADDRESS; CALL BDOS$P( 9, S );    END;
   PR$NL:     PROCEDURE; CALL PR$STRING( .( 0DH, NL$CHAR, '$' ) );       END;
   FL$EXISTS: PROCEDURE( FCB )BYTE; /* RETURNS TRUE IF THE FILE NAMED IN THE */
      DECLARE FCB ADDRESS;          /*                 FCB EXISTS            */
      RETURN ( BDOS( 17, FCB ) < 4 );
   END FL$EXISTS ;
   FL$OPEN:   PROCEDURE( FCB )BYTE; /* OPEN THE FILE WITH THE SPECIFIED FCB  */
      DECLARE FCB ADDRESS;
      RETURN ( BDOS( 15, FCB ) < 4 );
   END FL$OPEN;
   FL$MAKE:   PROCEDURE( FCB )BYTE; /* CREATE AND OPEN THE FILE WITH THE     */
      DECLARE FCB ADDRESS;          /* SPECIFIED FCB                         */
      RETURN ( BDOS( 22, FCB ) < 4 );
   END FL$MAKE;
   FL$READ:   PROCEDURE( FCB )BYTE; /* READ THE NEXT RECORD FROM FCB         */
      DECLARE FCB ADDRESS;
      RETURN ( BDOS( 20, FCB ) = 0 );
   END FL$READ;
   FL$WRITE:  PROCEDURE( FCB )BYTE; /* WRITE A RECORD TO FCB                 */
      DECLARE FCB ADDRESS;
      RETURN ( BDOS( 21, FCB ) = 0 );
   END FL$WRITE;
   FL$CLOSE:  PROCEDURE( FCB )BYTE; /* CLOSE THE FILE WITH THE SPECIFIED FCB */
      DECLARE FCB ADDRESS;
      RETURN ( BDOS( 16, FCB ) < 4 );
   END FL$CLOSE;
   DMA$SET:   PROCEDURE( DMA );     /* SET THE DMA BUFFER ADDRESS FOR I/O    */
      DECLARE DMA ADDRESS;
      CALL BDOS$P( 26, DMA );
   END DMA$SET;
   /* I/O USES FILE CONTROL BLOCKS CONTAINING THE FILE-NAME, POSITION, ETC.  */
   /* WHEN THE PROGRAM IS RUN, THE CCP WILL FIRST PARSE THE COMMAND LINE AND */
   /* PUT THE FIRST PARAMETER IN FCB1, THE SECOND PARAMETER IN FCB2          */
   /* BUT FCB2 OVERLAYS THE END OF FCB1 AND THE DMA BUFFER OVERLAYS THE END  */
   /* OF FCB2                                                                */

   DECLARE FCB$SIZE      LITERALLY '36';  /* SIZE OF A FCB                   */
   DECLARE FCB1          LITERALLY '5CH'; /* ADDRESS OF FIRST  FCB           */
   DECLARE FCB2          LITERALLY '6CH'; /* ADDRESS OF SECOND FCB           */
   DECLARE DMA$BUFFER    LITERALLY '80H'; /* DEFAULT DMA BUFFER ADDRESS      */
   DECLARE DMA$SIZE      LITERALLY '128'; /* SIZE OF THE DMA BUFFER          */

   INIT$FCB: PROCEDURE( FCB ); /* INITIALISE A FILE-CONTROL-BLOCK */
      DECLARE FCB ADDRESS;
      DECLARE F$PTR ADDRESS;
      DECLARE F BASED F$PTR BYTE, P BYTE;
      F$PTR = FCB;
      F = 0;                                         /* DEFAULT DRIVE */
      DO F$PTR = FCB + 1 TO FCB + 11;                /* NO NAME */
         F = ' ';
      END;
      DO F$PTR = FCB + 12 TO FCB + ( FCB$SIZE - 1 ); /* OTHER FIELDS */
         F = 0;
      END;
   END INIT$FCB;
   MOVE$FCB: PROCEDURE( FROM$FCB, TO$FCB ); /* MOVE THE CONTENTS OF AN FCB   */
      DECLARE ( FROM$FCB, TO$FCB ) ADDRESS;
      DECLARE ( F$PTR, T$PTR ) ADDRESS;
      DECLARE F BASED F$PTR BYTE, T BASED T$PTR BYTE, P BYTE;
      CALL INIT$FCB( TO$FCB );
      F$PTR = FROM$FCB;
      T$PTR = TO$FCB;
      DO P =  0 TO 11; /* COPY DRIVE, FILENAME AND EXTENSION */
         T = F;
         F$PTR = F$PTR + 1;
         T$PTR = T$PTR + 1;
      END;
   END MOVE$FCB;
   SHOW$FCB: PROCEDURE( FCB );                /* SHOW THE CONTENTS OF AN FCB */
      DECLARE FCB   ADDRESS;
      DECLARE F$PTR ADDRESS;
      DECLARE F BASED F$PTR BYTE, P BYTE;
      F$PTR = FCB;
      DO P =  0 TO 11;                      /* DRIVE, FILENAME AND EXTENSION */
         IF P = 9 THEN CALL PR$CHAR( '.' );
         IF P = 1 THEN CALL PR$CHAR( ':' );
         CALL PR$CHAR( F );
         F$PTR = F$PTR + 1;
      END;
   END SHOW$FCB;

   DECLARE F$PTR ADDRESS, F$CHAR BASED F$PTR BYTE;
   DECLARE W$PTR ADDRESS, W$CHAR BASED W$PTR BYTE;
   DECLARE FCB$OUT$DATA   ( FCB$SIZE )BYTE;
   DECLARE OUT$DMA        ( DMA$SIZE )BYTE;
   DECLARE OUT$BUFFER     LITERALLY '.OUT$DMA';
   DECLARE FCB$IN         LITERALLY 'FCB1';
   DECLARE FCB$OUT        LITERALLY '.FCB$OUT$DATA';

   DECLARE K              LITERALLY 'CALL ADDKW';

   DECLARE KW             ( 34 )ADDRESS;
   DECLARE KW$MAX         BYTE;

   KW$MAX = -1;
   ADDKW: PROCEDURE( ADDR );                         /* ADDS A KEYWORD TO KW */
      DECLARE ADDR ADDRESS;
      KW( KW$MAX := KW$MAX + 1 ) = ADDR;
   END ADDKW ;

   K(.'ADDRESS$');K(.'AND$');K(.'BASED$');K(.'BY$');K(.'BYTE$');K(.'CALL$');
   K(.'CASE$');K(.'DATA$');K(.'DECLARE$');K(.'DISABLE$');K(.'DO$');K(.'ELSE$');
   K(.'ENABLE$');K(.'END$');K(.'EOF$');K(.'GO$');K(.'GOTO$');K(.'HALT$');
   K(.'IF$');K(.'INITIAL$');K(.'INTERRUPT$');K(.'LABEL$');K(.'LITERALLY$');
   K(.'MINUS$');K(.'MOD$');K(.'NOT$');K(.'OR$');K(.'PLUS$');K(.'PROCEDURE$');
   K(.'RETURN$');K(.'THEN$');K(.'TO$');K(.'WHILE$');K(.'XOR$');
  
   /* MOVE THE SECOND FCB TO A NEW PLACE SO IT ISN'T OVERWRITTEN BY FCB1     */
   CALL MOVE$FCB( FCB2, FCB$OUT );

   /* CLEAR THE PARTS OF FCB1 OVERLAYED BY FCB2                              */
   DO F$PTR = FCB1 + 12 TO FCB1 + ( FCB$SIZE - 1 );
      F$CHAR = 0;
   END;

   STR$EQUAL: PROCEDURE( S1, S2 )BYTE;             /* RETURN TRUE IF S1 = S2 */
      DECLARE ( S1, S2 ) ADDRESS;
      DECLARE ( S1$PTR, S2$PTR ) ADDRESS;
      DECLARE C1 BASED S1$PTR BYTE, C2 BASED S2$PTR BYTE, SAME BYTE;
      S1$PTR = S1; S2$PTR = S2;
      DO WHILE ( SAME := C1 = C2 ) AND C1 <> '$' AND C2 <> '$';
         S1$PTR = S1$PTR + 1; S2$PTR = S2$PTR + 1;
      END;
      RETURN SAME;
   END STR$EQUAL ;

   IS$WORD$CHAR: PROCEDURE( CH )BYTE; /* RETURN TRUE IF CH IS PART OF A WORD */
      DECLARE CH BYTE;
      RETURN ( CH >= 'A' AND CH <= 'Z' ) OR CH = '$'
          OR ( CH >= '0' AND CH <= '9' );
   END IS$WORD$CHAR ;
          
   IF NOT FL$EXISTS( FCB$IN ) THEN DO;
      CALL SHOW$FCB( FCB$IN );
      CALL PR$STRING( .': INPUT FILE NOT FOUND$' );CALL PR$NL;
      END;
   ELSE IF FL$EXISTS( FCB$OUT ) THEN DO;
      CALL SHOW$FCB(  FCB$OUT );
      CALL PR$STRING( .': OUTPUT FILE ALREADY EXISTS$' );CALL PR$NL;
      END;
   ELSE IF NOT FL$OPEN( FCB$IN ) THEN DO;
      CALL PR$STRING( .'UNABLE TO OPEN THE INPUT FILE$' );CALL PR$NL;
      END;
   ELSE IF NOT FL$MAKE( FCB$OUT ) THEN DO;
      CALL PR$STRING( .'UNABLE TO OPEN THE OUTPUT FILE$' );CALL PR$NL;
      IF NOT FL$CLOSE( FCB$IN ) THEN DO;
         CALL PR$STRING( .'UNABLE TO CLOSE THE INPUT FILE$' ); CALL PR$NL;
      END;
      END;
   ELSE DO; /* FILES OPENED OK - ATTEMPT TO FORMAT THE SOURCE                */
      DECLARE ( GOT$RCD, IS$HEADING ) BYTE, ( DMA$END, OUT$END ) ADDRESS;
      DECLARE IN$STRING BYTE, COMMENT$STATE ADDRESS, GOT$NEXT BYTE;

      IN$CHAR: PROCEDURE;
         F$PTR = F$PTR + 1;
         IF F$PTR > DMA$END THEN DO;                        /* END OF BUFFER */
            GOT$RCD = FL$READ( FCB$IN );             /* GET THE NEXT RECORDD */
            IF NOT GOT$RCD THEN F$CHAR = EOF$CHAR;
            F$PTR   = DMA$BUFFER;
         END;
      END IN$CHAR ;
      OUT$CHAR: PROCEDURE( CH );   /* OUTPUT A CHARECTER TO THE OUTPOUT FILE */
         DECLARE CH BYTE;
         IF CH <> EOF$CHAR THEN CALL PR$CHAR( CH );
         W$CHAR = CH;
         W$PTR  = W$PTR + 1;
         IF W$PTR > OUT$END OR CH = EOF$CHAR THEN DO;
            /* THE OUTPUT BUFFER IS FULL OR WE ARE WRITTING EOF              */
            IF CH = EOF$CHAR THEN DO;     /* EOF - FILL THE BUFFER WITH NULS */
               DO WHILE W$PTR <= OUT$END;
                  W$CHAR = 0;
                  W$PTR  = W$PTR + 1;
               END;
            END;
            CALL DMA$SET( OUT$BUFFER );   /* SWITCH DMA TO THE OUTOUT BUFFER */
            IF NOT FL$WRITE( FCB$OUT ) THEN DO;                 /* I/O ERROR */
               CALL PR$STRING( .'I/O ERROR ON WRITING $' );
               CALL SHOW$FCB( FCB$OUT );
               CALL PR$NL;
               CALL EXIT;
            END;
            CALL DMA$SET( DMA$BUFFER );   /* RESET DMA TO THE DEFAULT BUFFER */
            W$PTR = OUT$BUFFER;
         END;
      END OUT$CHAR;
      OUT$STRING: PROCEDURE( STR );                       /* OUTPUT A STRING */
         DECLARE STR    ADDRESS;
         DECLARE S$PTR  ADDRESS;
         DECLARE S$CHAR BASED S$PTR BYTE;
         S$PTR = STR;
         DO WHILE S$CHAR <> '$';
            CALL OUT$CHAR( S$CHAR );
            S$PTR = S$PTR + 1;
         END;
      END OUT$STRING;

      DMA$END       = DMA$BUFFER + ( DMA$SIZE - 1 );
      OUT$END       = OUT$BUFFER + ( DMA$SIZE - 1 );
      GOT$RCD       = FL$READ( FCB$IN );             /* GET THE FIRST RECORD */
      F$PTR         = DMA$BUFFER;
      W$PTR         = OUT$BUFFER;
      IN$STRING     = FALSE;
      GOT$NEXT      = FALSE;
      COMMENT$STATE = 0;
      CALL OUT$CHAR( ' ' );
      DO WHILE GOT$RCD;
         IF F$CHAR = CR$CHAR THEN DO;                     /* CARRIAGE RETURN */
            IF COMMENT$STATE > 1 THEN DO;
               COMMENT$STATE = 2;
               CALL OUT$STRING( .'''''$' );
            END;
            CALL OUT$CHAR( F$CHAR );
            END;
         ELSE IF F$CHAR = NL$CHAR THEN DO;
            CALL OUT$CHAR( F$CHAR );
            CALL OUT$CHAR( ' ' );
            IF COMMENT$STATE > 1 THEN CALL OUT$STRING( .'''''$' );
            END;
         ELSE IF F$CHAR = AMP THEN DO;
            CALL OUT$STRING( .( AMP, LCA, LCM, LCP, ';$' ) );
            END;
         ELSE IF F$CHAR = '''' THEN DO;
            CALL OUT$STRING( .( AMP, LCA, LCP, LCO, LCS, ';$' ) );
            IN$STRING = COMMENT$STATE = 0 AND NOT IN$STRING;
            END;
         ELSE IF F$CHAR = '<' THEN DO;
            CALL OUT$STRING( .( AMP, LCL, LCT, ';$' ) );
            END;
         ELSE IF F$CHAR = '>' THEN DO;
            CALL OUT$STRING( .( AMP, LCG, LCT, ';$' ) );
            END;
         ELSE IF IN$STRING THEN CALL OUT$CHAR( F$CHAR );
         ELSE IF COMMENT$STATE = 1 THEN DO; /* HAVE A CHARACTER AFTER /      */
            IF F$CHAR = '*' THEN DO;
               COMMENT$STATE = 2;
               CALL OUT$STRING( .'''''/*$' );
               END;
            ELSE DO;
               COMMENT$STATE = 0;
               CALL OUT$CHAR( '/' );
               CALL OUT$CHAR( F$CHAR );
            END;
            END;
         ELSE IF COMMENT$STATE = 2 THEN DO;                  /* IN A COMMENT */
            IF F$CHAR = '*' THEN COMMENT$STATE = 3;
            CALL OUT$CHAR( F$CHAR );
            END;
         ELSE IF COMMENT$STATE = 3 THEN DO;     /* IN A COMMENT, EXPECTING / */
            IF F$CHAR = '/' THEN DO;                       /* END OF COMMENT */
               CALL OUT$STRING( .'/''''$' );
               COMMENT$STATE = 0;
               END;
            ELSE DO;                                   /* NOT END OF COMMENT */
               CALL OUT$CHAR( F$CHAR );
               IF F$CHAR <> '*' THEN COMMENT$STATE = 2;
            END;
            END;
         ELSE IF F$CHAR = '/' THEN DO;
            IF      COMMENT$STATE = 0 THEN COMMENT$STATE = 1;
            ELSE IF COMMENT$STATE = 3 THEN DO;
               /* END OF COMMENT                                             */
               CALL OUT$STRING( .'/''''$' );
               COMMENT$STATE = 0;
               END;
            ELSE CALL OUT$CHAR( F$CHAR );
            END;
         ELSE IF F$CHAR = EOF$CHAR THEN GOT$RCD  = FALSE;     /* END OF FILE */
         ELSE IF F$CHAR >= 'A' AND F$CHAR <= 'Z' THEN DO;            /* WORD */
             DECLARE W    ( 10 )BYTE, W$POS BYTE, HAS$DOLLAR BYTE;
             OUT$WORD: PROCEDURE;           /* OUTPUT W (WHICH MAY CONTAIN $ */
                DECLARE I BYTE;
                DO I = 0 TO W$POS - 1; CALL OUT$CHAR( W( I ) ); END;
             END OUT$WORD ;
             W$POS = 0;
             HAS$DOLLAR = FALSE;
             DO WHILE W$POS < 9 AND IS$WORD$CHAR( F$CHAR );
                IF F$CHAR = '$' THEN HAS$DOLLAR = TRUE;
                W( W$POS ) = F$CHAR; W$POS = W$POS + 1; CALL IN$CHAR;
             END;
             W( W$POS ) = '$';
             IF IS$WORD$CHAR( F$CHAR ) THEN DO;    /* WORD IS TOO LONG FOE A */
                CALL OUT$WORD;                             /* KEYWORD */
                DO WHILE IS$WORD$CHAR( F$CHAR );
                   CALL OUT$CHAR( F$CHAR );CALL IN$CHAR;
                END;
                END;
             ELSE IF HAS$DOLLAR THEN DO;        /* ASSUME IT ISN'T A KEYWORD */
                CALL OUT$WORD;  /* I.E., THE PROGRAMMER HASN'T WRITTEN E.G.: */
                END;                                           /* RE$TURN X; */
             ELSE DO;                     /* SHORT WORD - COULD BE A KEYWORD */
                DECLARE ( IS$KW, KW$POS ) BYTE;
                IS$KW  = FALSE;
                KW$POS = 0;
                DO WHILE NOT IS$KW AND KW$POS <= KW$MAX;
                   IS$KW  = STR$EQUAL( .W, KW( KW$POS ) );
                   KW$POS = KW$POS + 1;
                END;
                IF IS$KW THEN CALL OUT$STRING( .'''''''$' );
                CALL OUT$WORD;
                IF IS$KW THEN CALL OUT$STRING( .'''''''$' );
             END;
             GOT$NEXT = TRUE;
             END;
         ELSE DO;                                  /* HAVE ANOTHER CHARACTER */               
             CALL OUT$CHAR( F$CHAR );
         END;
         IF NOT GOT$NEXT THEN CALL IN$CHAR;
         GOT$NEXT = FALSE;
      END;
      CALL OUT$CHAR( EOF$CHAR );
      /* CLOSE THE FILES                                                     */
      IF NOT FL$CLOSE( FCB$IN ) THEN DO;
         CALL PR$STRING( .'UNABLE TO CLOSE THE INPUT FILE$' ); CALL PR$NL;
      END;
      IF NOT FL$CLOSE( FCB$OUT ) THEN DO;
         CALL PR$STRING( .'UNABLE TO CLOSE THE OUTPUT FILE$' ); CALL PR$NL;
      END;
   END;

   CALL EXIT;

EOF

Python

Library: pygments

This solution builds on lexers available in Pygments by defining a formatter outputting simple MediaWiki markup, and a filter to translate characters to HTML escape sequences. Note that I've taken liberties with said escaping.

"""Syntax highlighting using Mediawiki formatting."""
from html import escape
from textwrap import indent
from io import StringIO

from pygments import highlight
from pygments.filter import Filter
from pygments.formatter import Formatter
from pygments.lexers import get_lexer_by_name
from pygments.token import Token


class MediaWikiFormatter(Formatter):
    """Format source code using MediaWiki markup."""

    name = "MediaWiki"
    aliases = ["mediawiki", "wiki"]
    filenames = []

    def __init__(self, **options):
        super().__init__(**options)
        self.indent = options.get("indent", " ")

        self.styles = {
            Token: ("", ""),
            Token.Comment: ("''", "''"),
            Token.Keyword: ("'''", "'''"),
            Token.String.Doc: ("''", "''"),
        }

    def format(self, token_source, outfile):
        buffer = StringIO()
        last_val = ""
        last_type = None

        for token_type, value in token_source:
            # Work up the token hierarchy until a style is found.
            while token_type not in self.styles:
                token_type = token_type.parent

            # Group consecutive tokens of the same type.
            if token_type == last_type:
                last_val += value
            else:
                if last_val:
                    style_begin, style_end = self.styles[last_type]
                    buffer.write(style_begin + last_val + style_end)

                last_val = value
                last_type = token_type

        # Flush remaining values.
        if last_val:
            style_begin, style_end = self.styles[last_type]
            buffer.write(style_begin + last_val + style_end)

        # Write indented lines to the output file.
        outfile.write(
            indent(
                buffer.getvalue(),
                self.indent,
                lambda _: True,
            )
        )


class HTMLEscapeFilter(Filter):
    """Convert the characters &, <, > and ' to HTML-safe sequences."""

    def __init__(self, **options):
        super().__init__(**options)

    def filter(self, _, stream):
        for ttype, value in stream:
            yield ttype, escape(value)


def main(language_name="python", infile=None):
    formatter = MediaWikiFormatter()
    lexer = get_lexer_by_name(language_name)
    lexer.add_filter(HTMLEscapeFilter())

    with open(infile or __file__) as fd:
        print(highlight(fd.read(), lexer, formatter), end="")


if __name__ == "__main__":
    main()

Wren

Library: Wren-ioutil

Note that, rightly or wrongly, this code would not highlight keywords occurring in interpolated string expressions.

// Convert a Wren source to "wiki" format:
//   each line is preceded by a space
//   keywords are enclosed in ''' and ''' and comments in '' and ''
//   ', &, < and > are converted to &apos; &amp; &lt; and &gt;
//   everything else is output as is
// The source is read from a file and written to standard output.
// The file name should be passed as a command line argument.

import "./ioutil" for FileUtil
import "os" for Process

var keywords = [
    "as", "break", "class", "construct", "continue", "else", "false",
    "for", "foreign", "if", "in", "is", "import", "null", "return", 
    "static", "super", "this", "true", "var", "while"
]

var alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_."

var highlight = Fn.new { |lines|
    var inStr = false  // within a string literal
    var inRaw = false  // within a raw string literal
    var inCom = false  // within a multi-line comment
    var level = 0      // nesting level for multi-line comment
    for (line in lines) {
        System.write(" ")
        line = line.replace("&", "&amp;").replace("'", "&apos;")
                   .replace("<", "&lt;").replace(">", "&gt;")
        var word = ""
        var chrs = line.toList // convert to list of unicode characters
        var cc = chrs.count
        var i = 0
        if (inCom) System.write("''")
        while (i < cc) {
            var c = chrs[i]
            if (inCom) { // if inside a multi-line comment
                if (c == "/" && i < cc-1 && chrs[i+1] == "*") {
                    level = level + 1
                    System.write("/*")
                    i = i + 1
                } else if (c == "*" && i < cc-1 && chrs[i+1] == "/") {
                    level = level - 1
                    System.write("*/")
                    i = i + 1
                    if (level == 0) {
                        inCom = false
                        System.write("''")
                    }
                } else {
                    System.write(c)
                }
            } else if (inStr && c == "\\" && i < cc-1 && chrs[i+1] == "\"") {
                /* escaped double quote in string literal */
                System.write("\\\"")
                i = i + 1
            } else if (c == "\"") { // any other double quote
                if (i > 1 && chrs[i-2] == "\"" && chrs[i-1] == "\"") {
                    inRaw = !inRaw
                } else if (!inRaw) {
                    inStr = !inStr
                }
                System.write("\"")
            } else if (inStr || inRaw) { // otherwise if within a string just write it
                System.write(c)
            } else if (c == "/") { // forward slash
                if (i < cc-1 && chrs[i+1] == c) {
                    System.write("''" + chrs[i..-1].join() + "''")
                    break
                } else if (i < cc-1 && chrs[i+1] == "*") {
                    inCom = true
                    level = 1
                    System.write("''" + "/*")
                    i = i + 1
                } else {
                    System.write(c)
                }
            } else if (alphabet.contains(c)) { // if eligible, add to current word
                word = word + c
            } else if (keywords.contains(word)) { // if it's a keyword, embolden it
                System.write("'''" + word + "'''" + c)
                word = ""
            } else { // otherwise just write the word
                System.write(word + c)
                word = ""
            }
            i = i + 1
        }
        if (inCom) {
            System.write("''")
        } else if (word != "") {
            if (keywords.contains(word)) {
                System.write("'''" + word + "'''")
            } else {
                System.write(word)
            }
        }
        System.print()
    }
}

var args = Process.arguments
if (args.count != 1) {
    /* make sure double quotes and keywords in raw strings are handled properly */
    Fiber.abort("""Please pass the file name to be highlighted "as" the only argument.""")
}
var lines = FileUtil.readLines(args[0])
highlight.call(lines)
/* this code should now be saved to
   /* a file named */
   Syntax_highlighting_using_Mediawiki_formatting.wren */

XPL0

Key words in XPL0 are easy to distinguish from variable names because they start with lowercase letters while variables start with uppercase letters. This program highlights its own code properly, but it will not properly highlight all possible cases, such as when key words appear in quoted strings.

proc CharOut(Ch);
int  Ch;
begin
case Ch of
  ^':   Text(0, "&apos;");
  ^&:   Text(0, "&amp;");
  ^<:   Text(0, "&lt;");
  ^>:   Text(0, "&gt;")
other   ChOut(1, Ch);
end;

int  Ch;
loop begin
ChOut(1, $20);                          \leading space
Ch:= ChIn(1);
loop    begin
        while Ch <= $20 do              \pass whitespace to output
                begin
                if Ch = $1A \EOF\ then return;
                ChOut(1, Ch);
                if Ch = $0A \LF\ then quit;
                Ch:= ChIn(1);
                end;
        if Ch = ^\ then                 \pass comment to output
                begin
                Text(0, "''");          \in italics
                ChOut(1, Ch);
                Ch:= ChIn(1);
                while Ch#^\ & Ch#$0A \LF\ do
                        begin
                        CharOut(Ch);  Ch:= ChIn(1);
                        end;
                if Ch = ^\ then ChOut(1, Ch);
                Text(0, "''");
                if Ch = $0A \LF\ then
                        begin
                        ChOut(1, Ch);  quit;
                        end;
                Ch:= ChIn(1);
                end
        else if Ch>=^a & Ch<=^z then    \pass key words to output
                begin
                Text(0, "'''");         \in bold
                while Ch>=^a & Ch<=^z do
                        begin
                        ChOut(1, Ch);  Ch:= ChIn(1);
                        end;
                Text(0, "'''");
                end
        else    begin                   \pass anything else
                repeat  CharOut(Ch);
                        Ch:= ChIn(1);
                until Ch <= $20;        \until whitespace
                end;
        end;
end