Skip to content

Commit

Permalink
Add GAP console session mode (#2211)
Browse files Browse the repository at this point in the history
* Add GAP console session mode

This is also appropriate for GAP .tst files.

Add `analyse_text` methods for `ScilabLexer` and `GAPConsoleLexer` to
distinguish Scilab and GAP .tst files

* Use explicit name for 'keepends' argument to splitlines
  • Loading branch information
fingolfin committed Aug 19, 2022
1 parent adb90dc commit f497654
Show file tree
Hide file tree
Showing 11 changed files with 1,843 additions and 5 deletions.
1 change: 1 addition & 0 deletions pygments/lexers/_mapping.py
Expand Up @@ -168,6 +168,7 @@
'FoxProLexer': ('pygments.lexers.foxpro', 'FoxPro', ('foxpro', 'vfp', 'clipper', 'xbase'), ('*.PRG', '*.prg'), ()),
'FreeFemLexer': ('pygments.lexers.freefem', 'Freefem', ('freefem',), ('*.edp',), ('text/x-freefem',)),
'FutharkLexer': ('pygments.lexers.futhark', 'Futhark', ('futhark',), ('*.fut',), ('text/x-futhark',)),
'GAPConsoleLexer': ('pygments.lexers.algebra', 'GAP session', ('gap-console', 'gap-repl'), ('*.tst',), ()),
'GAPLexer': ('pygments.lexers.algebra', 'GAP', ('gap',), ('*.g', '*.gd', '*.gi', '*.gap'), ()),
'GDScriptLexer': ('pygments.lexers.gdscript', 'GDScript', ('gdscript', 'gd'), ('*.gd',), ('text/x-gdscript', 'application/x-gdscript')),
'GLShaderLexer': ('pygments.lexers.graphics', 'GLSL', ('glsl',), ('*.vert', '*.frag', '*.geo'), ('text/x-glslsrc',)),
Expand Down
63 changes: 60 additions & 3 deletions pygments/lexers/algebra.py
Expand Up @@ -10,11 +10,11 @@

import re

from pygments.lexer import RegexLexer, bygroups, words
from pygments.lexer import Lexer, RegexLexer, bygroups, do_insertions, words
from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
Number, Punctuation, Whitespace
Number, Punctuation, Generic, Whitespace

__all__ = ['GAPLexer', 'MathematicaLexer', 'MuPADLexer', 'BCLexer']
__all__ = ['GAPLexer', 'GAPConsoleLexer', 'MathematicaLexer', 'MuPADLexer', 'BCLexer']


class GAPLexer(RegexLexer):
Expand Down Expand Up @@ -88,6 +88,63 @@ def analyse_text(text):
return min(score, 1.0)


class GAPConsoleLexer(Lexer):
"""
For GAP console sessions. Modeled after JuliaConsoleLexer.
.. versionadded:: 2.14
"""
name = 'GAP session'
aliases = ['gap-console', 'gap-repl']
filenames = ['*.tst']

def get_tokens_unprocessed(self, text):
gaplexer = GAPLexer(**self.options)
start = 0
curcode = ''
insertions = []
output = False
error = False

for line in text.splitlines(keepends=True):
if line.startswith('gap> ') or line.startswith('brk> '):
insertions.append((len(curcode), [(0, Generic.Prompt, line[:5])]))
curcode += line[5:]
output = False
error = False
elif not output and line.startswith('> '):
insertions.append((len(curcode), [(0, Generic.Prompt, line[:2])]))
curcode += line[2:]
else:
if curcode:
yield from do_insertions(
insertions, gaplexer.get_tokens_unprocessed(curcode))
curcode = ''
insertions = []
if line.startswith('Error, ') or error:
yield start, Generic.Error, line
error = True
else:
yield start, Generic.Output, line
output = True
start += len(line)

if curcode:
yield from do_insertions(
insertions, gaplexer.get_tokens_unprocessed(curcode))

# the following is needed to distinguish Scilab and GAP .tst files
def analyse_text(text):
# GAP prompts are a dead give away, although hypothetical;y a
# file in another language could be trying to compare a variable
# "gap" as in "gap> 0.1". But that this should happen at the
# start of a line seems unlikely...
if re.search(r"^gap> ", text):
return 0.9
else:
return 0.0


class MathematicaLexer(RegexLexer):
"""
Lexer for Mathematica source code.
Expand Down
2 changes: 1 addition & 1 deletion pygments/lexers/julia.py
Expand Up @@ -252,7 +252,7 @@ def get_tokens_unprocessed(self, text):
output = False
error = False

for line in text.splitlines(True):
for line in text.splitlines(keepends=True):
if line.startswith('julia>'):
insertions.append((len(curcode), [(0, Generic.Prompt, line[:6])]))
curcode += line[6:]
Expand Down
2 changes: 1 addition & 1 deletion pygments/lexers/make.py
Expand Up @@ -44,7 +44,7 @@ class MakefileLexer(Lexer):

def get_tokens_unprocessed(self, text):
ins = []
lines = text.splitlines(True)
lines = text.splitlines(keepends=True)
done = ''
lex = BaseMakefileLexer(**self.options)
backslashflag = False
Expand Down
12 changes: 12 additions & 0 deletions pygments/lexers/matlab.py
Expand Up @@ -3294,3 +3294,15 @@ class ScilabLexer(RegexLexer):
(r'(\s*)([a-zA-Z_]\w*)', bygroups(Text, Name.Function), '#pop'),
],
}

# the following is needed to distinguish Scilab and GAP .tst files
def analyse_text(text):
score = 0.0

# Scilab comments (don't appear in e.g. GAP code)
if re.search(r"^\s*//", text):
score += 0.1
if re.search(r"^\s*/\*", text):
score += 0.1

return min(score, 1.0)
60 changes: 60 additions & 0 deletions tests/examplefiles/gap-repl/euclidean.tst
@@ -0,0 +1,60 @@
#@local checkEuclideanRing
gap> START_TEST("euclidean.tst");

# test consistency of EuclideanDegree, EuclideanQuotient, EuclideanRemainder,
# and QuotientRemainder for some ring and elements of it
gap> checkEuclideanRing :=
> function(R, colls...)
> local coll1, coll2, a, b, deg_b, deg_r, q, r, qr;
> if Length(colls) >= 1 then coll1:=colls[1];
> elif Size(R) <= 100 then coll1 := R;
> else coll1 := List([1..100],i->Random(R));
> fi;
> if Length(colls) >= 2 then coll2:=colls[2];
> elif Size(R) <= 100 then coll2 := R;
> else coll2 := List([1..100],i->Random(R));
> fi;
> for b in coll1 do
> if IsZero(b) then continue; fi;
> deg_b := EuclideanDegree(R, b);
> for a in coll2 do
> q := EuclideanQuotient(R, a, b); Assert(0, q in R);
> r := EuclideanRemainder(R, a, b); Assert(0, r in R);
> if a <> q*b + r then Error("a <> q*b + r for ", [R,a,b]); fi;
> deg_r := EuclideanDegree(R, r);
> if not IsZero(r) and deg_r >= deg_b then Error("Euclidean degree did not decrease for ",[R,a,b]); fi;
> qr := QuotientRemainder(R, a, b);
> if qr <> [q, r] then Error("QuotientRemainder inconsistent for ", [R,a,b]); fi;
> od;
> od;
> return true;
> end;;

# rings in characteristic 0
gap> checkEuclideanRing(Integers,[-100..100],[-100..100]);
true
gap> checkEuclideanRing(Rationals);
true
gap> checkEuclideanRing(GaussianIntegers);
true
gap> checkEuclideanRing(GaussianRationals);
true

# finite fields
gap> ForAll(Filtered([2..50], IsPrimePowerInt), q->checkEuclideanRing(GF(q)));
true

# ZmodnZ
gap> ForAll([1..50], m -> checkEuclideanRing(Integers mod m));
true
gap> checkEuclideanRing(Integers mod ((2*3*5)^2));
true
gap> checkEuclideanRing(Integers mod ((2*3*5)^3));
true
gap> checkEuclideanRing(Integers mod ((2*3*5*7)^2));
true
gap> checkEuclideanRing(Integers mod ((2*3*5*7)^3));
true

#
gap> STOP_TEST( "euclidean.tst", 1);

0 comments on commit f497654

Please sign in to comment.