summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--CHANGES1
-rw-r--r--pygments/lexers/_mapping.py1
-rw-r--r--pygments/lexers/compiled.py198
-rw-r--r--tests/examplefiles/Sorting.mod470
-rw-r--r--tests/examplefiles/test.mod374
6 files changed, 1044 insertions, 1 deletions
diff --git a/AUTHORS b/AUTHORS
index 02fab08e..d102686a 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -34,6 +34,7 @@ Other contributors, listed alphabetically, are:
* David Hess, Fish Software, Inc. -- Objective-J lexer
* Varun Hiremath -- Debian control lexer
* Dennis Kaarsemaker -- sources.list lexer
+* Benjamin Kowarsch -- Modula-2 lexer
* Marek Kubica -- Scheme lexer
* Jochen Kupperschmidt -- Markdown processor
* Gerd Kurzbach -- Modelica lexer
diff --git a/CHANGES b/CHANGES
index 1d034fc6..3c3daaaf 100644
--- a/CHANGES
+++ b/CHANGES
@@ -14,6 +14,7 @@ Version 1.3
* Ada
* Coldfusion
+ * Modula-2
* haXe
* R console
* Objective-J
diff --git a/pygments/lexers/_mapping.py b/pygments/lexers/_mapping.py
index f90251d0..cdaf56a1 100644
--- a/pygments/lexers/_mapping.py
+++ b/pygments/lexers/_mapping.py
@@ -121,6 +121,7 @@ LEXERS = {
'MatlabSessionLexer': ('pygments.lexers.math', 'Matlab session', ('matlabsession',), (), ()),
'MiniDLexer': ('pygments.lexers.agile', 'MiniD', ('minid',), ('*.md',), ('text/x-minidsrc',)),
'ModelicaLexer': ('pygments.lexers.other', 'Modelica', ('modelica',), ('*.mo',), ('text/x-modelica',)),
+ 'Modula2Lexer': ('pygments.lexers.compiled', 'Modula-2', ('modula2', 'm2'), ('*.def', '*.mod'), ('text/x-modula2',)),
'MoinWikiLexer': ('pygments.lexers.text', 'MoinMoin/Trac Wiki markup', ('trac-wiki', 'moin'), (), ('text/x-trac-wiki',)),
'MuPADLexer': ('pygments.lexers.math', 'MuPAD', ('mupad',), ('*.mu',), ()),
'MxmlLexer': ('pygments.lexers.web', 'MXML', ('mxml',), ('*.mxml',), ()),
diff --git a/pygments/lexers/compiled.py b/pygments/lexers/compiled.py
index a6069611..cf2bc737 100644
--- a/pygments/lexers/compiled.py
+++ b/pygments/lexers/compiled.py
@@ -25,7 +25,8 @@ from pygments.lexers.functional import OcamlLexer
__all__ = ['CLexer', 'CppLexer', 'DLexer', 'DelphiLexer', 'JavaLexer',
'ScalaLexer', 'DylanLexer', 'OcamlLexer', 'ObjectiveCLexer',
'FortranLexer', 'GLShaderLexer', 'PrologLexer', 'CythonLexer',
- 'ValaLexer', 'OocLexer', 'GoLexer', 'FelixLexer', 'AdaLexer']
+ 'ValaLexer', 'OocLexer', 'GoLexer', 'FelixLexer', 'AdaLexer',
+ 'Modula2Lexer']
class CLexer(RegexLexer):
@@ -2169,3 +2170,198 @@ class AdaLexer(RegexLexer):
include('root'),
],
}
+
+
+class Modula2Lexer(RegexLexer):
+ """
+ For `Modula-2 <http://www.modula2.org/>`_ source code.
+
+ Additional options that determine which keywords are highlighted:
+
+ `pim`
+ Select PIM Modula-2 dialect (default: True).
+ `iso`
+ Select ISO Modula-2 dialect (default: False).
+ `objm2`
+ Select Objective Modula-2 dialect (default: False).
+ `gm2ext`
+ Also highlight GNU extensions (default: False).
+
+ *New in Pygments 1.3.*
+ """
+ name = 'Modula-2'
+ aliases = ['modula2', 'm2']
+ filenames = ['*.def', '*.mod']
+ mimetypes = ['text/x-modula2']
+
+ flags = re.MULTILINE | re.DOTALL
+
+ _ws = r'(?:\s|//.*?\n|/[*].*?[*]/)+'
+
+ tokens = {
+ 'whitespace': [
+ (r'\n+', Text), # blank lines
+ (r'\s+', Text), # whitespace
+ ],
+ 'identifiers': [
+ (r'([a-zA-Z_\$][a-zA-Z0-9_\$]*)', Name),
+ ],
+ 'numliterals': [
+ (r'[01]+B', Number.Binary), # binary number (ObjM2)
+ (r'[0-7]+B', Number.Oct), # octal number (PIM + ISO)
+ (r'[0-7]+C', Number.Oct), # char code (PIM + ISO)
+ (r'[0-9A-F]+C', Number.Hex), # char code (ObjM2)
+ (r'[0-9A-F]+H', Number.Hex), # hexadecimal number
+ (r'[0-9]+\.[0-9]+E[+-][0-9]+', Number.Float), # real number
+ (r'[0-9]+\.[0-9]+', Number.Float), # real number
+ (r'[0-9]+', Number.Integer), # decimal whole number
+ ],
+ 'strings': [
+ (r"'(\\\\|\\'|[^'])*'", String), # single quoted string
+ (r'"(\\\\|\\"|[^"])*"', String), # double quoted string
+ ],
+ 'operators': [
+ (r'[*/+=#~&<>\^-]', Operator),
+ (r':=', Operator), # assignment
+ (r'@', Operator), # pointer deref (ISO)
+ (r'\.\.', Operator), # ellipsis or range
+ (r'`', Operator), # Smalltalk message (ObjM2)
+ (r'::', Operator), # type conversion (ObjM2)
+ ],
+ 'punctuation': [
+ (r'[\(\)\[\]{},.:;|]', Punctuation),
+ ],
+ 'comments': [
+ (r'//.*?\n', Comment.Single), # ObjM2
+ (r'/\*(.*?)\*/', Comment.Multiline), # ObjM2
+ (r'\(\*([^\$].*?)\*\)', Comment.Multiline),
+ # TO DO: nesting of (* ... *) comments
+ ],
+ 'pragmas': [
+ (r'\(\*\$(.*?)\*\)', Comment.Preproc), # PIM
+ (r'<\*(.*?)\*>', Comment.Preproc), # ISO + ObjM2
+ ],
+ 'root': [
+ include('whitespace'),
+ include('comments'),
+ include('pragmas'),
+ include('identifiers'),
+ include('numliterals'),
+ include('strings'),
+ include('operators'),
+ include('punctuation'),
+ ]
+ }
+
+ pim_reserved_words = [
+ # 40 reserved words
+ 'AND', 'ARRAY', 'BEGIN', 'BY', 'CASE', 'CONST', 'DEFINITION',
+ 'DIV', 'DO', 'ELSE', 'ELSIF', 'END', 'EXIT', 'EXPORT', 'FOR',
+ 'FROM', 'IF', 'IMPLEMENTATION', 'IMPORT', 'IN', 'LOOP', 'MOD',
+ 'MODULE', 'NOT', 'OF', 'OR', 'POINTER', 'PROCEDURE', 'QUALIFIED',
+ 'RECORD', 'REPEAT', 'RETURN', 'SET', 'THEN', 'TO', 'TYPE',
+ 'UNTIL', 'VAR', 'WHILE', 'WITH',
+ ]
+
+ pim_pervasives = [
+ # 31 pervasives
+ 'ABS', 'BITSET', 'BOOLEAN', 'CAP', 'CARDINAL', 'CHAR', 'CHR', 'DEC',
+ 'DISPOSE', 'EXCL', 'FALSE', 'FLOAT', 'HALT', 'HIGH', 'INC', 'INCL',
+ 'INTEGER', 'LONGINT', 'LONGREAL', 'MAX', 'MIN', 'NEW', 'NIL', 'ODD',
+ 'ORD', 'PROC', 'REAL', 'SIZE', 'TRUE', 'TRUNC', 'VAL',
+ ]
+
+ iso_reserved_words = [
+ # 46 reserved words
+ 'AND', 'ARRAY', 'BEGIN', 'BY', 'CASE', 'CONST', 'DEFINITION', 'DIV',
+ 'DO', 'ELSE', 'ELSIF', 'END', 'EXCEPT', 'EXIT', 'EXPORT', 'FINALLY',
+ 'FOR', 'FORWARD', 'FROM', 'IF', 'IMPLEMENTATION', 'IMPORT', 'IN',
+ 'LOOP', 'MOD', 'MODULE', 'NOT', 'OF', 'OR', 'PACKEDSET', 'POINTER',
+ 'PROCEDURE', 'QUALIFIED', 'RECORD', 'REPEAT', 'REM', 'RETRY',
+ 'RETURN', 'SET', 'THEN', 'TO', 'TYPE', 'UNTIL', 'VAR', 'WHILE',
+ 'WITH',
+ ]
+
+ iso_pervasives = [
+ # 42 pervasives
+ 'ABS', 'BITSET', 'BOOLEAN', 'CAP', 'CARDINAL', 'CHAR', 'CHR', 'CMPLX',
+ 'COMPLEX', 'DEC', 'DISPOSE', 'EXCL', 'FALSE', 'FLOAT', 'HALT', 'HIGH',
+ 'IM', 'INC', 'INCL', 'INT', 'INTEGER', 'INTERRUPTIBLE', 'LENGTH',
+ 'LFLOAT', 'LONGCOMPLEX', 'LONGINT', 'LONGREAL', 'MAX', 'MIN', 'NEW',
+ 'NIL', 'ODD', 'ORD', 'PROC', 'PROTECTION', 'RE', 'REAL', 'SIZE',
+ 'TRUE', 'TRUNC', 'UNINTERRUBTIBLE', 'VAL',
+ ]
+
+ objm2_reserved_words = [
+ # base language, 42 reserved words
+ 'AND', 'ARRAY', 'BEGIN', 'BY', 'CASE', 'CONST', 'DEFINITION', 'DIV',
+ 'DO', 'ELSE', 'ELSIF', 'END', 'ENUM', 'EXIT', 'FOR', 'FROM', 'IF',
+ 'IMMUTABLE', 'IMPLEMENTATION', 'IMPORT', 'IN', 'IS', 'LOOP', 'MOD',
+ 'MODULE', 'NOT', 'OF', 'OPAQUE', 'OR', 'POINTER', 'PROCEDURE',
+ 'RECORD', 'REPEAT', 'RETURN', 'SET', 'THEN', 'TO', 'TYPE',
+ 'UNTIL', 'VAR', 'VARIADIC', 'WHILE',
+ # OO extensions, 16 reserved words
+ 'BYCOPY', 'BYREF', 'CLASS', 'CONTINUE', 'CRITICAL', 'INOUT', 'METHOD',
+ 'ON', 'OPTIONAL', 'OUT', 'PRIVATE', 'PROTECTED', 'PROTOCOL', 'PUBLIC',
+ 'SUPER', 'TRY',
+ ]
+
+ objm2_pervasives = [
+ # base language, 38 pervasives
+ 'ABS', 'BITSET', 'BOOLEAN', 'CARDINAL', 'CHAR', 'CHR', 'DISPOSE',
+ 'FALSE', 'HALT', 'HIGH', 'INTEGER', 'INRANGE', 'LENGTH', 'LONGCARD',
+ 'LONGINT', 'LONGREAL', 'MAX', 'MIN', 'NEG', 'NEW', 'NEXTV', 'NIL',
+ 'OCTET', 'ODD', 'ORD', 'PRED', 'PROC', 'READ', 'REAL', 'SUCC', 'TMAX',
+ 'TMIN', 'TRUE', 'TSIZE', 'UNICHAR', 'VAL', 'WRITE', 'WRITEF',
+ # OO extensions, 3 pervasives
+ 'OBJECT', 'NO', 'YES',
+ ]
+
+ gnu_reserved_words = [
+ # 10 additional reserved words
+ 'ASM', '__ATTRIBUTE__', '__BUILTIN__', '__COLUMN__', '__DATE__',
+ '__FILE__', '__FUNCTION__', '__LINE__', '__MODULE__', 'VOLATILE',
+ ]
+
+ gnu_pervasives = [
+ # 21 identifiers, actually from pseudo-module SYSTEM
+ # but we will highlight them as if they were pervasives
+ 'BITSET8', 'BITSET16', 'BITSET32', 'CARDINAL8', 'CARDINAL16',
+ 'CARDINAL32', 'CARDINAL64', 'COMPLEX32', 'COMPLEX64', 'COMPLEX96',
+ 'COMPLEX128', 'INTEGER8', 'INTEGER16', 'INTEGER32', 'INTEGER64',
+ 'REAL8', 'REAL16', 'REAL32', 'REAL96', 'REAL128', 'THROW',
+ ]
+
+ def __init__(self, **options):
+ self.reserved_words = set()
+ self.pervasives = set()
+ # ISO Modula-2
+ if get_bool_opt(options, 'iso', False):
+ self.reserved_words.update(self.iso_reserved_words)
+ self.pervasives.update(self.iso_pervasives)
+ # Objective Modula-2
+ elif get_bool_opt(options, 'objm2', False):
+ self.reserved_words.update(self.objm2_reserved_words)
+ self.pervasives.update(self.objm2_pervasives)
+ # PIM Modula-2 (DEFAULT)
+ else:
+ self.reserved_words.update(self.pim_reserved_words)
+ self.pervasives.update(self.pim_pervasives)
+ # GNU extensions
+ if get_bool_opt(options, 'gm2ext', False):
+ self.reserved_words.update(self.gnu_reserved_words)
+ self.pervasives.update(self.gnu_pervasives)
+ # initialise
+ RegexLexer.__init__(self, **options)
+
+ def get_tokens_unprocessed(self, text):
+ for index, token, value in \
+ RegexLexer.get_tokens_unprocessed(self, text):
+ # check for reserved words and pervasives
+ if token is Name:
+ if value in self.reserved_words:
+ token = Keyword.Reserved
+ elif value in self.pervasives:
+ token = Keyword.Pervasive
+ # return result
+ yield index, token, value
diff --git a/tests/examplefiles/Sorting.mod b/tests/examplefiles/Sorting.mod
new file mode 100644
index 00000000..d6a27c1f
--- /dev/null
+++ b/tests/examplefiles/Sorting.mod
@@ -0,0 +1,470 @@
+IMPLEMENTATION MODULE Sorting;
+
+(* J. Andrea, Dec.16/91 *)
+(* This code may be freely used and distributed, it may not be sold. *)
+
+(* Adapted to ISO Module-2 by Frank Schoonjans Feb 2004 *)
+
+FROM Storage IMPORT ALLOCATE;
+
+CONST
+ max_stack = 20;
+ n_small = 6; (* use a simple sort for this size and smaller *)
+
+VAR
+ rtemp :REAL;
+ ctemp :CARDINAL;
+
+ L, R, n :INTEGER;
+ top, bottom, lastflip :INTEGER;
+
+ tos :CARDINAL;
+ Lstack, Rstack :ARRAY [1..max_stack] OF INTEGER;
+
+ (* --------------------------------------------------- *)
+ PROCEDURE CardQSortIndex( x :ARRAY OF CARDINAL; array_len :CARDINAL;
+ VAR index :ARRAY OF CARDINAL );
+
+ VAR
+ median : CARDINAL;
+ i,j : INTEGER;
+ BEGIN
+
+ n := VAL(INTEGER,array_len) - 1; (* back to zero offset *)
+
+ (* initialize the index *)
+ FOR i := 0 TO n DO
+ index[i] := VAL(CARDINAL,i);
+ END;
+
+ tos := 0;
+
+ L := 0; R := n;
+
+ (* PUSH very first set *)
+ tos := tos + 1; Lstack[tos] := L; Rstack[tos] := R;
+
+ REPEAT
+
+ (* POP *)
+ L := Lstack[tos]; R := Rstack[tos]; tos := tos - 1;
+
+ IF R - L + 1 > n_small THEN
+
+ REPEAT
+ i := L; j := R; median := x[index[( L + R ) DIV 2]];
+
+ REPEAT
+ WHILE x[index[i]] < median DO
+ i := i + 1;
+ END;
+ WHILE median < x[index[j]] DO
+ j := j - 1;
+ END;
+
+ IF i <= j THEN (* swap *)
+ ctemp := index[i]; index[i] := index[j]; index[j] := ctemp;
+ i := i + 1; j := j - 1;
+ END;
+ UNTIL i > j;
+
+ IF j - L < R - i THEN
+ IF i < R THEN (* PUSH *)
+ tos := tos + 1; Lstack[tos] := i; Rstack[tos] := R;
+ END;
+ R := j;
+ ELSE
+ IF L < j THEN (* push *)
+ tos := tos + 1; Lstack[tos] := L; Rstack[tos] := j;
+ END;
+ L := i;
+ END;
+
+ UNTIL L >= R;
+
+ ELSE
+
+ (* small sort for small number of values *)
+ FOR i := L TO R - 1 DO
+ FOR j := i TO R DO
+ IF x[index[i]] > x[index[j]] THEN
+ ctemp := index[i];
+ index[i] := index[j];
+ index[j] := ctemp
+ END;
+ END;
+ END;
+
+ END; (* check for small *)
+
+ UNTIL tos = 0;
+
+ END CardQSortIndex;
+
+ (* --------------------------------------------------- *)
+ PROCEDURE RealQSortIndex( x :ARRAY OF REAL; array_len :CARDINAL;
+ VAR index :ARRAY OF CARDINAL );
+
+ VAR
+ median :REAL;
+ i,j :INTEGER;
+ BEGIN
+
+ n := VAL(INTEGER,array_len) - 1; (* back to zero offset *)
+
+ (* initialize the index *)
+ FOR i := 0 TO n DO
+ index[i] := VAL(CARDINAL,i);
+ END;
+
+ tos := 0;
+
+ L := 0; R := n;
+
+ (* PUSH very first set *)
+ tos := tos + 1; Lstack[tos] := L; Rstack[tos] := R;
+
+ REPEAT
+
+ (* POP *)
+ L := Lstack[tos]; R := Rstack[tos]; tos := tos - 1;
+
+ IF R - L + 1 > n_small THEN
+
+ REPEAT
+ i := L; j := R; median := x[index[( L + R ) DIV 2]];
+
+ REPEAT
+ WHILE x[index[i]] < median DO
+ i := i + 1;
+ END;
+ WHILE median < x[index[j]] DO
+ j := j - 1;
+ END;
+
+ IF i <= j THEN (* swap *)
+ ctemp := index[i]; index[i] := index[j]; index[j] := ctemp;
+ i := i + 1; j := j - 1;
+ END;
+ UNTIL i > j;
+
+ IF j - L < R - i THEN
+ IF i < R THEN (* PUSH *)
+ tos := tos + 1; Lstack[tos] := i; Rstack[tos] := R;
+ END;
+ R := j;
+ ELSE
+ IF L < j THEN (* push *)
+ tos := tos + 1; Lstack[tos] := L; Rstack[tos] := j;
+ END;
+ L := i;
+ END;
+
+ UNTIL L >= R;
+
+ ELSE
+
+ (* small sort for small number of values *)
+ FOR i := L TO R - 1 DO
+ FOR j := i TO R DO
+ IF x[index[i]] > x[index[j]] THEN
+ ctemp := index[i];
+ index[i] := index[j];
+ index[j] := ctemp
+ END;
+ END;
+ END;
+
+ END; (* check for small *)
+
+ UNTIL tos = 0;
+
+ END RealQSortIndex;
+
+ (* --------------------------------------------------- *)
+ PROCEDURE CardQSort( VAR x :ARRAY OF CARDINAL; array_len :CARDINAL );
+
+ VAR
+ median : CARDINAL;
+ n,i,j : INTEGER;
+ BEGIN
+
+ n := VAL(INTEGER,array_len) - 1; (* back to zero offset *)
+
+ tos := 0;
+
+ L := 0; R := n;
+
+ (* PUSH very first set *)
+ tos := tos + 1; Lstack[tos] := L; Rstack[tos] := R;
+
+ REPEAT
+
+ (* POP *)
+ L := Lstack[tos]; R := Rstack[tos]; tos := tos - 1;
+
+ IF R - L + 1 > n_small THEN
+
+ REPEAT
+ i := L; j := R; median := x[( L + R ) DIV 2];
+
+ REPEAT
+ WHILE x[i] < median DO
+ i := i + 1;
+ END;
+ WHILE median < x[j] DO
+ j := j - 1;
+ END;
+
+ IF i <= j THEN (* swap *)
+ ctemp := x[i]; x[i] := x[j]; x[j] := ctemp;
+ i := i + 1; j := j - 1;
+ END;
+ UNTIL i > j;
+
+ IF j - L < R - i THEN
+ IF i < R THEN (* PUSH *)
+ tos := tos + 1; Lstack[tos] := i; Rstack[tos] := R;
+ END;
+ R := j;
+ ELSE
+ IF L < j THEN (* push *)
+ tos := tos + 1; Lstack[tos] := L; Rstack[tos] := j;
+ END;
+ L := i;
+ END;
+
+ UNTIL L >= R;
+
+ ELSE
+
+ (* small sort for small number of values *)
+ FOR i := L TO R - 1 DO
+ FOR j := i TO R DO
+ IF x[i] > x[j] THEN
+ ctemp := x[i];
+ x[i] := x[j];
+ x[j] := ctemp
+ END;
+ END;
+ END;
+
+ END; (* check for small *)
+
+ UNTIL tos = 0;
+
+ END CardQSort;
+
+ (* ----------------------------------------------------- *)
+ PROCEDURE CardBSort( VAR x :ARRAY OF CARDINAL; array_len :CARDINAL );
+ VAR i,j : INTEGER;
+ BEGIN
+ top := 0; (* open arrays are zero offset *)
+ bottom := VAL(INTEGER,array_len) - 1;
+
+ WHILE top < bottom DO
+
+ lastflip := top;
+
+ FOR i := top TO bottom-1 DO
+ IF x[i] > x[i+1] THEN (* flip *)
+ ctemp := x[i];
+ x[i] := x[i+1];
+ x[i+1] := ctemp;
+ lastflip := i;
+ END;
+ END;
+
+ bottom := lastflip;
+
+ IF bottom > top THEN
+
+ i := bottom - 1;
+ FOR j := top TO bottom-1 DO
+ IF x[i] > x[i+1] THEN (* flip *)
+ ctemp := x[i];
+ x[i] := x[i+1];
+ x[i+1] := ctemp;
+ lastflip := i;
+ END;
+ i := i - 1;
+ END;
+
+ top := lastflip + 1;
+
+ ELSE
+ (* force a loop failure *)
+ top := bottom + 1;
+ END;
+
+ END;
+
+ END CardBSort;
+
+
+ (* ----------------------------------------------------- *)
+ PROCEDURE RealBSort( VAR x :ARRAY OF REAL; array_len :CARDINAL );
+ VAR bottom,top : INTEGER;
+ i,j : INTEGER;
+ BEGIN
+ top := 0; (* open arrays are zero offset *)
+ bottom := VAL(INTEGER,array_len) - 1;
+
+ WHILE top < bottom DO
+
+ lastflip := top;
+
+ FOR i := top TO bottom-1 DO
+ IF x[i] > x[i+1] THEN (* flip *)
+ rtemp := x[i];
+ x[i] := x[i+1];
+ x[i+1] := rtemp;
+ lastflip := i;
+ END;
+ END;
+
+ bottom := lastflip;
+
+ IF bottom > top THEN
+
+ i := bottom - 1;
+ FOR j := top TO bottom-1 DO
+ IF x[i] > x[i+1] THEN (* flip *)
+ rtemp := x[i];
+ x[i] := x[i+1];
+ x[i+1] := rtemp;
+ lastflip := i;
+ END;
+ i := i - 1;
+ END;
+
+ top := lastflip + 1;
+
+ ELSE
+ (* force a loop failure *)
+ top := bottom + 1;
+ END;
+
+ END;
+
+ END RealBSort;
+
+
+ (* ----------------------------------------------------- *)
+ PROCEDURE TopoSort( x, y :ARRAY OF CARDINAL; n_pairs :CARDINAL;
+ VAR solution :ARRAY OF CARDINAL; VAR n_solution :CARDINAL;
+ VAR error, sorted :BOOLEAN );
+ (*
+ This procedure needs some garbage collection added, i've tried but
+ will little success. J. Andrea, Dec.18/91
+ *)
+
+ TYPE
+ LPtr = POINTER TO Leader;
+ TPtr = POINTER TO Trailer;
+
+ Leader = RECORD
+ key :CARDINAL;
+ count :INTEGER;
+ trail :TPtr;
+ next :LPtr;
+ END;
+
+ Trailer = RECORD
+ id :LPtr;
+ next :TPtr;
+ END;
+
+ VAR
+ p, q, head, tail :LPtr;
+ t :TPtr;
+ i, max_solutions :CARDINAL;
+
+ (* -------------------------------------------- *)
+ PROCEDURE Find( w :CARDINAL ) :LPtr;
+ VAR h :LPtr;
+ BEGIN
+ h := head; tail^.key := w; (* sentinel *)
+ WHILE h^.key # w DO
+ h := h^.next;
+ END;
+ IF h = tail THEN
+ NEW( tail );
+ n := n + 1;
+ h^.count := 0;
+ h^.trail := NIL;
+ h^.next := tail;
+ END;
+ RETURN h;
+ END Find;
+
+ BEGIN
+
+ error := FALSE;
+ n_solution := 0;
+
+ IF n_pairs < 2 THEN
+ error := TRUE;
+ ELSE
+
+ max_solutions := HIGH( solution ) + 1;
+
+ NEW( head ); tail := head; n := 0;
+
+ (* add all of the given pairs *)
+
+ FOR i := 0 TO n_pairs - 1 DO
+ p := Find( x[i] ); q := Find( y[i] );
+ NEW(t);
+ t^.id := q;
+ t^.next := p^.trail;
+ p^.trail := t;
+ q^.count := q^.count + 1;
+ END;
+
+ (* search for leaders without predecessors *)
+
+ p := head; head := NIL;
+ WHILE p # tail DO
+ q := p; p := q^.next;
+ IF q^.count = 0 THEN
+ (* insert q^ in new chain *)
+ q^.next := head; head := q;
+ END;
+ END;
+
+ (* output phase *)
+
+ q := head;
+ WHILE ( NOT error ) & ( q # NIL ) DO
+ n_solution := n_solution + 1;
+ IF n_solution > max_solutions THEN
+ error := TRUE;
+ ELSE
+
+ solution[n_solution-1] := q^.key;
+ n := n - 1;
+ t := q^.trail; q := q^.next;
+ WHILE t # NIL DO
+ p := t^.id; p^.count := p^.count - 1;
+ IF p^.count = 0 THEN
+ (* insert p^ in leader list *)
+ p^.next := q; q := p;
+ END;
+ t := t^.next;
+ END;
+ END;
+ END;
+
+ IF n # 0 THEN
+ sorted := FALSE;
+ ELSE
+ sorted := TRUE;
+ END;
+
+ END;
+
+ END TopoSort;
+
+BEGIN
+END Sorting.
diff --git a/tests/examplefiles/test.mod b/tests/examplefiles/test.mod
new file mode 100644
index 00000000..ba972e30
--- /dev/null
+++ b/tests/examplefiles/test.mod
@@ -0,0 +1,374 @@
+(* LIFO Storage Library
+ *
+ * @file LIFO.mod
+ * LIFO implementation
+ *
+ * Universal Dynamic Stack
+ *
+ * Author: Benjamin Kowarsch
+ *
+ * Copyright (C) 2009 Benjamin Kowarsch. All rights reserved.
+ *
+ * License:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met
+ *
+ * 1) NO FEES may be charged for the provision of the software. The software
+ * may NOT be published on websites that contain advertising, unless
+ * specific prior written permission has been obtained.
+ *
+ * 2) Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 3) Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and other materials provided with the distribution.
+ *
+ * 4) Neither the author's name nor the names of any contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * 5) Where this list of conditions or the following disclaimer, in part or
+ * as a whole is overruled or nullified by applicable law, no permission
+ * is granted to use the software.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ *)
+
+
+IMPLEMENTATION (* OF *) MODULE LIFO;
+
+FROM SYSTEM IMPORT ADDRESS, ADR, TSIZE;
+FROM Storage IMPORT ALLOCATE, DEALLOCATE;
+
+
+(* ---------------------------------------------------------------------------
+// Private type : ListEntry
+// ---------------------------------------------------------------------------
+*)
+TYPE ListPtr = POINTER TO ListEntry;
+
+TYPE ListEntry = RECORD
+ value : DataPtr;
+ next : ListPtr
+END; (* ListEntry *)
+
+
+(* ---------------------------------------------------------------------------
+// Opaque type : LIFO.Stack
+// ---------------------------------------------------------------------------
+// CAUTION: Modula-2 does not support the use of variable length array fields
+// in records. VLAs can only be implemented using pointer arithmetic which
+// means there is no type checking and no boundary checking on the array.
+// It also means that array notation cannot be used on the array which makes
+// the code difficult to read and maintain. As a result, Modula-2 is less
+// safe and less readable than C when it comes to using VLAs. Great care must
+// be taken to make sure that the code accessing VLA fields is safe. Boundary
+// checks must be inserted manually. Size checks must be inserted manually to
+// compensate for the absence of type checks. *)
+
+TYPE Stack = POINTER TO StackDescriptor;
+
+TYPE StackDescriptor = RECORD
+ overflow : ListPtr;
+ entryCount : StackSize;
+ arraySize : StackSize;
+ array : ADDRESS (* ARRAY OF DataPtr *)
+END; (* StackDescriptor *)
+
+
+(* ---------------------------------------------------------------------------
+// function: LIFO.new( initial_size, status )
+// ---------------------------------------------------------------------------
+//
+// Creates and returns a new LIFO stack object with an initial capacity of
+// <initialSize>. If zero is passed in for <initialSize>, then the stack
+// will be created with an initial capacity of LIFO.defaultStackSize. The
+// function fails if a value greater than LIFO.maximumStackSize is passed
+// in for <initialSize> or if memory could not be allocated.
+//
+// The initial capacity of a stack is the number of entries that can be stored
+// in the stack without enlargement.
+//
+// The status of the operation is passed back in <status>. *)
+
+PROCEDURE new ( initialSize : StackSize; VAR status : Status ) : Stack;
+
+VAR
+ newStack : Stack;
+
+BEGIN
+
+ (* zero size means default *)
+ IF initialSize = 0 THEN
+ initialSize := defaultStackSize;
+ END; (* IF *)
+
+ (* bail out if initial size is too high *)
+ IF initialSize > maximumStackSize THEN
+ status := invalidSize;
+ RETURN NIL;
+ END; (* IF *)
+
+ (* allocate new stack object *)
+ ALLOCATE(newStack, TSIZE(Stack) + TSIZE(DataPtr) * (initialSize - 1));
+
+ (* bail out if allocation failed *)
+ IF newStack = NIL THEN
+ status := allocationFailed;
+ RETURN NIL;
+ END; (* IF *)
+
+ (* initialise meta data *)
+ newStack^.arraySize := initialSize;
+ newStack^.entryCount := 0;
+ newStack^.overflow := NIL;
+
+ (* pass status and new stack to caller *)
+ status := success;
+ RETURN newStack
+
+END new;
+
+
+(* ---------------------------------------------------------------------------
+// function: LIFO.push( stack, value, status )
+// ---------------------------------------------------------------------------
+//
+// Adds a new entry <value> to the top of stack <stack>. The new entry is
+// added by reference, no data is copied. However, no entry is added if the
+// the stack is full, that is when the number of entries stored in the stack
+// has reached LIFO.maximumStackSize. The function fails if NIL is passed in
+// for <stack> or <value>, or if memory could not be allocated.
+//
+// New entries are allocated dynamically if the number of entries exceeds the
+// initial capacity of the stack.
+//
+// The status of the operation is passed back in <status>. *)
+
+PROCEDURE push ( VAR stack : Stack; value : DataPtr; VAR status : Status );
+VAR
+ newEntry : ListPtr;
+ valuePtr : POINTER TO DataPtr;
+
+BEGIN
+
+ (* bail out if stack is NIL *)
+ IF stack = NIL THEN
+ status := invalidStack;
+ RETURN;
+ END; (* IF *)
+
+ (* bail out if value is NIL *)
+ IF value = NIL THEN
+ status := invalidData;
+ RETURN;
+ END; (* IF *)
+
+ (* bail out if stack is full *)
+ IF stack^.entryCount >= maximumStackSize THEN
+ status := stackFull;
+ RETURN;
+ END; (* IF *)
+
+ (* check if index falls within array segment *)
+ IF stack^.entryCount < stack^.arraySize THEN
+
+ (* store value in array segment *)
+
+ (* stack^.array^[stack^.entryCount] := value; *)
+ valuePtr := ADR(stack^.array) + TSIZE(DataPtr) * stack^.entryCount;
+ valuePtr^ := value;
+
+ ELSE (* index falls within overflow segment *)
+
+ (* allocate new entry slot *)
+ NEW(newEntry);
+
+ (* bail out if allocation failed *)
+ IF newEntry = NIL THEN
+ status := allocationFailed;
+ RETURN;
+ END; (* IF *)
+
+ (* initialise new entry *)
+ newEntry^.value := value;
+
+ (* link new entry into overflow list *)
+ newEntry^.next := stack^.overflow;
+ stack^.overflow := newEntry;
+
+ END; (* IF *)
+
+ (* update entry counter *)
+ INC(stack^.entryCount);
+
+ (* pass status to caller *)
+ status := success;
+ RETURN
+
+END push;
+
+
+(* ---------------------------------------------------------------------------
+// function: LIFO.pop( stack, status )
+// ---------------------------------------------------------------------------
+//
+// Removes the top most value from stack <stack> and returns it. If the stack
+// is empty, that is when the number of entries stored in the stack has
+// reached zero, then NIL is returned.
+//
+// Entries which were allocated dynamically (above the initial capacity) are
+// deallocated when their values are popped.
+//
+// The status of the operation is passed back in <status>. *)
+
+PROCEDURE pop ( VAR stack : Stack; VAR status : Status ) : DataPtr;
+
+VAR
+ thisValue : DataPtr;
+ thisEntry : ListPtr;
+ valuePtr : POINTER TO DataPtr;
+
+BEGIN
+
+ (* bail out if stack is NIL *)
+ IF stack = NIL THEN
+ status := invalidStack;
+ RETURN NIL;
+ END; (* IF *)
+
+ (* bail out if stack is empty *)
+ IF stack^.entryCount = 0 THEN
+ status := stackEmpty;
+ RETURN NIL;
+ END; (* IF *)
+
+ DEC(stack^.entryCount);
+
+ (* check if index falls within array segment *)
+ IF stack^.entryCount < stack^.arraySize THEN
+
+ (* obtain value at index entryCount in array segment *)
+
+ (* thisValue := stack^.array^[stack^.entryCount]; *)
+ valuePtr := ADR(stack^.array) + TSIZE(DataPtr) * stack^.entryCount;
+ thisValue := valuePtr^;
+
+ ELSE (* index falls within overflow segment *)
+
+ (* obtain value of first entry in overflow list *)
+ thisValue := stack^.overflow^.value;
+
+ (* isolate first entry in overflow list *)
+ thisEntry := stack^.overflow;
+ stack^.overflow := stack^.overflow^.next;
+
+ (* remove the entry from overflow list *)
+ DISPOSE(thisEntry);
+
+ END; (* IF *)
+
+ (* return value and status to caller *)
+ status := success;
+ RETURN thisValue
+
+END pop;
+
+
+(* ---------------------------------------------------------------------------
+// function: LIFO.stackSize( stack )
+// ---------------------------------------------------------------------------
+//
+// Returns the current capacity of <stack>. The current capacity is the total
+// number of allocated entries. Returns zero if NIL is passed in for <stack>.
+*)
+PROCEDURE stackSize( VAR stack : Stack ) : StackSize;
+
+BEGIN
+
+ (* bail out if stack is NIL *)
+ IF stack = NIL THEN
+ RETURN 0;
+ END; (* IF *)
+
+ IF stack^.entryCount < stack^.arraySize THEN
+ RETURN stack^.arraySize;
+ ELSE
+ RETURN stack^.entryCount;
+ END; (* IF *)
+
+END stackSize;
+
+
+(* ---------------------------------------------------------------------------
+// function: LIFO.stackEntries( stack )
+// ---------------------------------------------------------------------------
+//
+// Returns the number of entries stored in stack <stack>, returns zero if
+// NIL is passed in for <stack>. *)
+
+PROCEDURE stackEntries( VAR stack : Stack ) : StackSize;
+
+BEGIN
+
+ (* bail out if stack is NIL *)
+ IF stack = NIL THEN
+ RETURN 0;
+ END; (* IF *)
+
+ RETURN stack^.entryCount
+
+END stackEntries;
+
+
+(* ---------------------------------------------------------------------------
+// function: LIFO.dispose( stack )
+// ---------------------------------------------------------------------------
+//
+// Disposes of LIFO stack object <stack>. Returns NIL. *)
+
+PROCEDURE dispose ( VAR stack : Stack ) : Stack;
+
+VAR
+ thisEntry : ListPtr;
+
+BEGIN
+
+ (* bail out if stack is NIL *)
+ IF stack = NIL THEN
+ RETURN NIL;
+ END; (* IF *)
+
+ (* deallocate any entries in stack's overflow list *)
+ WHILE stack^.overflow # NIL DO
+
+ (* isolate first entry in overflow list *)
+ thisEntry := stack^.overflow;
+ stack^.overflow := stack^.overflow^.next;
+
+ (* deallocate the entry *)
+ DISPOSE(thisEntry);
+
+ END; (* WHILE *)
+
+ (* deallocate stack object and pass NIL to caller *)
+ DEALLOCATE(stack, TSIZE(Stack) + TSIZE(DataPtr) * (stack^.arraySize - 1));
+ RETURN NIL
+
+END dispose;
+
+
+END LIFO.