Elident ("ELement IDENTity") is a self-consistency utility. It checks that a program is identical to the concatenation of the values of all its parsed elements.
Compound variables are checked part by part, and standard comments, doc comments, and resource data are handled specially.
If the check succeeds, Elident exits silently with exit code 0. If a difference is found, Elident displays the mismatching source and parsed lines and exits with exit code 1.
[rexx] elident [options] file
When called without arguments, display help information and exit.
-h, --help |
Display help and exit |
-e,
--experimental |
Enable Experimental features (also
-exp) |
-it,
--itrace |
Print internal traceback on error |
-u, --tutor,
--unicode |
Enable TUTOR-flavored Unicode |
-xtr,
--executor |
Enable Executor support |
#!/usr/bin/env rexx
/******************************************************************************/
/* */
/* elident.rex - Check that a program is equal to its Element API parsing */
/* ====================================================================== */
/* */
/* This program is part of the Rexx Parser package */
/* [See https://rexx.epbcn.com/rexx-parser/] */
/* */
/* Copyright (c) 2024-2026 Josep Maria Blasco <josep.maria.blasco@epbcn.com> */
/* */
/* License: Apache License 2.0 (https://www.apache.org/licenses/LICENSE-2.0) */
/* */
/* Version history: */
/* */
/* Date Version Details */
/* -------- ------- --------------------------------------------------------- */
/* 20241206 0.1 First public release */
/* 20241208 0.1a c/CLASSIC_COMMENT/STANDARD_COMMENT/ */
/* 20250328 0.2 Main dir is now rexx-parser instead of rexx[.]parser */
/* 20251110 0.3a Change the name to elident.rex */
/* 20251211 Add Executor support, move to /bin */
/* 20251218 Add TUTOR support */
/* 20251221 0.4a Add --itrace option, improve error messages */
/* 20251226 Send error messages to .error, not .output */
/* 20251227 Use .SysCArgs when available */
/* 20260103 Use compound variable parts */
/* 20260307 0.5 Implement experimental option */
/* 20260314 Use InitCLI() from CLISupport.cls */
/* */
/******************************************************************************/
Signal On Syntax
CLIhelper = InitCLI()
myName = CLIhelper~name
myHelp = CLIhelper~help
args = CLIhelper~args
executor = 0
experimental = 0
unicode = 0
itrace = 0
ProcessOptions:
If args~items == 0 Then Signal Help
option = args[1]
args~delete(1)
If option[1] == "-" Then Do
Select Case Lower(option)
When "-h", "--help" Then Signal Help
When "--executor", "-xtr" Then executor = 1
When "-e", "-exp", "--experimental" Then experimental = 1
When "-it", "--itrace" Then itrace = 1
When "-u", "--tutor", "--unicode" Then unicode = 1
Otherwise Call Error "Invalid option '"option"'."
End
Call ProcessOptions
End
If args~items > 0 Then Call Error "Invalid argument '"args[1]"'."
file = option
fullPath = .context~package~findProgram(file)
If fullPath == .Nil Then Call Error "File '"file"' does not exist."
-- Read the whole file into an array
chunk = CharIn(fullPath,1,Chars(fullPath))
Call Stream fullPath,"c","close"
source = chunk~makeArray
-- Makearray has a funny definition that ignores a possible
-- last empty line.
If Right(chunk,1) = "0a"X Then source~append("")
options = .Array~new
If executor Then options~append(("EXECUTOR", 1))
If unicode Then options~append(("UNICODE", 1))
If experimental Then options~append(("EXPERIMENTAL", 1))
parser = .Rexx.Parser~new( file, source, options )
currentLineNo = 1
currentLine = ""
element = parser~firstElement -- Same as parser~package~prolog~body~begin
Do Counter elements Until element == .Nil
If element~from \== element~to Then Do
category = element~category
elementLine = element~from~word(1)
If elementLine > currentLineNo Then Call ChangeLine
If category == .EL.STANDARD_COMMENT Then Call StandardComment
Else If category == .EL.DOC_COMMENT Then Call StandardComment
Else If category == .EL.DOC_COMMENT_MARKDOWN Then Call StandardComment
Else If category == .EL.RESOURCE_DATA Then Call ResourceData
Else If category == .EL.EXPOSED_COMPOUND_VARIABLE | -
category == .EL.COMPOUND_VARIABLE Then Call CompoundVariable
Else currentLine ||= element~source
End
element = element~next
End
Exit 0
Help:
Say .Resources[Help]~makeString -
~caselessChangeStr("myName", myName) -
~caselessChangeStr("myHelp", myHelp)
Exit 1
CompoundVariable:
Do part Over element~parts
currentLine ||= part~source
End
Return
StandardComment:
lastLine = element~to~word(1)
start = element~from~word(2)
end = element~ to~word(2)
If elementLine == lastLine Then Do
currentLine ||= source[currentLineNo][ start, end-start ]
Return
End
elementLine += 1
currentLine ||= SubStr( source[currentLineNo], start )
Call ChangeLine
currentLineNo = lastLine
currentLine = source[currentLineNo][1, end - 1]
Return
ResourceData:
lastLine = element~to~word(1)
currentLineNo = lastLine + 1
currentLine = ""
Return
ChangeLine:
Do While elementLine > currentLineNo
If source[currentLineNo] \== currentLine Then Do
Say( "Difference found in line number" currentLineNo":" )
Say "Source line is '"source[currentLineNo]"',"
Say "Parsed line is '"currentLine"'."
Exit 1
End
currentLineNo += 1
currentLine = ""
End
Return
--------------------------------------------------------------------------------
Error:
.Error~Say(Arg(1))
Exit 1
--------------------------------------------------------------------------------
-- Standard Rexx Parser error handler --
--------------------------------------------------------------------------------
Syntax:
co = condition("O")
If co~code \== 98.900 Then Do
.Error~Say( "Error" co~code "in" co~program", line" co~position":" )
Raise Propagate
End
Exit ErrorHandler( file, source, co, itrace)
::Resource HELP
myname -- Checks that the Parser' stream of elements is identical to a FILE.
Usage: myname [OPTION]... [FILE]
If the only option is -h or --help, or if no arguments are present,
then display this help and exit.
Options:
-it, --itrace Print internal trace on error
-u, --tutor, --unicode Enable TUTOR-flavored Unicode
-e, -exp, --experimental Enable Experimental features
-xtr, --executor Activate support for Executor language extensions
The 'myname' program is part of the Rexx Parser package,
see https://rexx.epbcn.com/rexx-parser/. It is distributed under
the Apache 2.0 License (https://www.apache.org/licenses/LICENSE-2.0).
Copyright (c) 2024-2026 Josep Maria Blasco <josep.maria.blasco@epbcn.com>.
See myHelp for details.
::END
::Requires "Rexx.Parser.cls"
::Requires "BaseClassesAndRoutines.cls"
::Requires "ErrorHandler.cls"
::Requires "CLISupport.cls"