diff --git a/PythonLatex/latex_structure.py b/PythonLatex/latex_structure.py
index d3631fd..516d984 100644
--- a/PythonLatex/latex_structure.py
+++ b/PythonLatex/latex_structure.py
@@ -1,357 +1,358 @@
#!/ usr / bin / python
################################################################
from __future__ import print_function
################################################################
import re
import types
import pyparsing as pp
################################################################
class LatexEnvironment(object):
def __init__(self, toks):
self.toks = toks
self.content = self.toks[1:-1]
self.head = self.toks[0]
self.tail = self.toks[-1]
self.name = self.head.toks[3]
self.hide = False
try:
self.option = self.head.toks[6]
except Exception as e:
self.option = None
# print('env: {0}:{1}:{2}'.format(self.name, self.option, self.content))
def __str__(self):
if self.hide:
return ''
return ''.join([str(self.head)]
+ [str(i) for i in self.content]
+ [str(self.tail)])
def __getitem__(self, index):
return self.toks[index]
class LatexCommand(object):
def __init__(self, toks):
self.name = toks[1]
self.toks = toks
# print('create_command:', self.name, toks)
def __str__(self):
return ''.join(self.toks)
def __getitem__(self, index):
return self.toks[index]
class LatexBlock(object):
def __init__(self, toks, name=None):
self.toks = toks
self.name = name
def __str__(self):
return ''.join([str(t) for t in self.toks])
class LatexMain(object):
def __init__(self, toks):
self.toks = toks
self.content = self.toks
self.name = "main"
def __str__(self):
# for i in self.toks:
# print('LatexMain:\n', type(i), str(i))
res = ''.join([str(t) for t in self.toks])
# print(res)
return res
def __getitem__(self, index):
return self.toks[index]
################################################################
class LatexStructure:
def getBlocksFromType(self, typ):
mylist = []
def foo(b):
if b.name == typ:
mylist.append(b)
self.pathInBlock(fbegin=foo)
return mylist
@staticmethod
def ppValidCharacters():
valid_characters = pp.printables
valid_characters = valid_characters.replace('%', '')
valid_characters = valid_characters.replace('{', '')
valid_characters = valid_characters.replace('}', '')
valid_characters = valid_characters.replace('\\', '')
valid_characters += ' \t\r\n'
return valid_characters
@property
def text(self):
if '_text' not in self.__dict__:
self._text = pp.Word(self.ppValidCharacters())
self._text = (self._text |
pp.Literal('\\\\') |
pp.Literal(r'\&') |
pp.Literal(r'\%') |
+ pp.Literal(r'\#') |
pp.Literal(r'\_'))
self._text.leaveWhitespace()
# self._text.addParseAction(lambda toks: print('text:', toks))
return self._text
@property
def comment(self):
if '_comment' not in self.__dict__:
self._comment = pp.Literal('%')
self._comment += pp.SkipTo(pp.LineEnd())
# self._comment.leaveWhitespace()
# self._comment.addParseAction(lambda toks: print('comment: ', toks))
return self._comment
@property
def block(self):
if '_block' not in self.__dict__:
_start = pp.Literal('{')
_end = pp.Literal('}')
_content = pp.Forward().leaveWhitespace()
_block = _start + _content + _end
self._block = _block
_content << pp.ZeroOrMore(self.environment() |
self.ppCommand() |
self.block |
self.text |
self.comment)
self._block.leaveWhitespace()
def createBlock(toks):
b = LatexBlock(toks)
return b
# _block.addParseAction(lambda toks: print('block:', toks))
_block.addParseAction(createBlock)
return self._block.leaveWhitespace()
@staticmethod
def ppCommand(name=None):
_command = pp.Literal('\\').leaveWhitespace()
if name is None:
_command += pp.Word(pp.alphanums + '@').leaveWhitespace()
else:
_command += pp.Literal(name).leaveWhitespace()
option = (
pp.Literal('[') +
pp.delimitedList(pp.Word(pp.alphanums),
combine=True) +
pp.Literal(']')).leaveWhitespace()
valid_param_character = pp.printables + ' \t\r\n\\'
valid_param_character = valid_param_character.replace('{', '')
valid_param_character = valid_param_character.replace('}', '')
param_name = pp.delimitedList(pp.Word(valid_param_character),
combine=True)
parameters = (
pp.Literal('{') + param_name + pp.Literal('}').leaveWhitespace()).leaveWhitespace()
_command += pp.ZeroOrMore(option | parameters).leaveWhitespace()
def createCommand(toks):
c = LatexCommand(toks)
return c
_command.addParseAction(createCommand)
# _command.addParseAction(lambda cmd: print('create_command:',
# cmd[0].name, cmd[0].toks))
return _command.leaveWhitespace()
def environment(self):
_env_start = self.ppCommand('begin')
_env_end = self.ppCommand('end')
_env_content = pp.Forward().leaveWhitespace()
_environment = _env_start + _env_content
_environment += _env_end
def set_excluding_command(toks):
env_name = toks[0][3]
# print('startenv:', env_name)
_command_excluding = self.ppCommand()
# print('command_excluding:', env_name)
if env_name == 'python':
python_block = pp.SkipTo(pp.Literal(r'\end{python}'))
_env_content << python_block
else:
_env_content << pp.ZeroOrMore(
self.environment().leaveWhitespace() |
_command_excluding |
self.block |
self.text |
self.comment)
def check(toks, env_name):
# print('check:', env_name)
if toks[0][1] != 'end':
return toks
# print('check:', toks, env_name)
# print('check2:', toks[0], env_name)
# print('check2:', type(toks[0]), env_name)
# print('check3:', toks[0][3], env_name)
# print('check2:', toks[0][3])
if toks[0][3] == env_name:
# print ('biiiiip')
return toks[652336456]
return toks
# _command_excluding.addParseAction(
# lambda toks: print('command_excluding:', toks))
_command_excluding.addParseAction(
lambda toks: check(toks, env_name))
# print('set_excluding_command: done')
_env_start.addParseAction(set_excluding_command)
# _env_content.addParseAction(lambda toks: print('found_content:', toks))
# _env_end.addParseAction(lambda toks: print('found_end:', toks[0][3]))
def createEnvironment(toks):
e = LatexEnvironment(toks)
return e
_environment.addParseAction(createEnvironment)
return _environment.leaveWhitespace()
def parseLatexFile(self, filename):
fin = open(filename, 'r')
inp = fin.read()
fin.close()
self.parseLatex(inp)
def parseLatex(self, latex_code):
_content = pp.ZeroOrMore(self.environment() |
self.block |
self.ppCommand() |
self.text |
self.comment)
_content.leaveWhitespace()
self._content = LatexMain(_content.parseString(latex_code))
def buildLatexBlocks(self, filename, herited_types=dict()):
fin = open(filename, 'r')
inp = fin.readlines()
inp = "".join(inp)
latex_cmd_expr = r'(\\\w+(?:\[\w*\])*(?:{[\w|,|\.|(|)]*?})+)'
splitted = re.split(latex_cmd_expr, inp)
self.main_block = LatexBlock()
self.current_block = self.main_block
for i in splitted:
m = re.match(r'\\begin{(.*?)}(.*)', i)
if m:
name = m.group(1)
options = m.group(2)
self.current_block = self.current_block.createSubBlock(
name, options, herited_types)
continue
m = re.match(r'\\end{(.*?)}', i)
if m:
name = m.group(1)
try:
self.current_block = self.current_block.endSubBlock(name)
except Exception as e:
print("AAAAAAAAAAAAAAAAAA")
print(e)
continue
self.current_block.appendContent(i)
if not self.current_block == self.main_block:
raise Exception(
"one latex block was not closed: {0}".format(
self.current_block.name))
def pathInBlockOld(self,
block=None,
begin_functor=None,
end_functor=None,
text_functor=None):
if block is None:
block = self.main_block
if begin_functor is not None:
begin_functor(block)
for i in range(len(block.content)):
c = block.content[i]
if isinstance(c, types.InstanceType):
try:
self.pathInBlock(c,
begin_functor,
end_functor,
text_functor)
except Exception as e:
print(e)
else:
if text_functor is not None:
block.content[i] = text_functor(block, c)
if end_functor is not None:
end_functor(block)
def pathInBlock(self,
block=None,
begin_functor=None,
end_functor=None,
text_functor=None):
if text_functor is None:
def text_functor(b, c):
return c
if block is None:
block = self._content
if begin_functor is not None:
begin_functor(block)
for i, c in enumerate(block.content):
# print(type(c), c)
if isinstance(c, LatexEnvironment):
self.pathInBlock(c,
begin_functor,
end_functor,
text_functor)
else:
block.content[i] = text_functor(block, str(c))
if end_functor is not None:
end_functor(block)
def __str__(self):
return str(self._content)
@property
def content(self):
return self._content
def __init__(self):
self._content = None
################################################################
diff --git a/Slides/snippet_helper.py b/Slides/snippet_helper.py
index 36eb1e7..9f765f9 100644
--- a/Slides/snippet_helper.py
+++ b/Slides/snippet_helper.py
@@ -1,329 +1,332 @@
#!/usr/bin/env python3
import subprocess
import os
import re
from pygments import highlight
from pygments.lexers import CppLexer
# from pygments.lexer import RegexLexer
# from pygments.lexers import MatlabLexer
from pygments.formatters import HtmlFormatter
from pygments.formatters import LatexFormatter
from pygments import token
from IPython.display import HTML
from tempfile import NamedTemporaryFile
################################################################
class Snippet(object):
class CompilationError(Exception):
""" Compilation error """
pass
default_output = 'html'
default_line_numbering = False
def __init__(self, inp, format_flag=False, compile_flag=False):
try:
self.loadfile(inp)
except Exception as e:
self.content = inp.strip()
if format_flag:
self.clang_format()
if compile_flag:
self.compile()
def save(self, filename):
f = open(filename, 'w')
f.write(self.content)
f.close()
def loadfile(self, inp):
filename = inp
full_filename = os.path.realpath(filename)
f = open(full_filename)
self.content = f.read()
f.close()
def clang_format(self):
f = NamedTemporaryFile()
f.write(self.content.encode())
f.flush()
p = subprocess.Popen(
'clang-format {0}'.format(f.name),
shell=True, stdout=subprocess.PIPE)
stdout, stderr = p.communicate()
formatted_file = stdout
self.content = formatted_file.decode('utf-8')
def compile(self, working_dir='/tmp'):
f = NamedTemporaryFile(suffix='.cpp', delete=False)
f.write(self.content.encode())
f.flush()
working_dir = os.path.dirname(f.name)
# file_dir = os.path.dirname(self.full_filename)
previous_dir = os.getcwd()
os.chdir(working_dir)
p = subprocess.Popen(
'g++ -Wall -Wextra -I {1} -c {0}'.format(f.name, working_dir),
shell=True, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
ret = p.returncode
os.chdir(previous_dir)
if ret != 0:
raise Snippet.CompilationError('compilation failed\n' + stderr.decode('utf-8'))
os.remove(f.name)
o_file = os.path.splitext(f.name)[0] + '.o'
os.remove(o_file)
def get_lines(self):
return self.content.split('\n')
def get_content(self):
return self.content
def __str__(self):
_content = self.content.split('\n')
ret = ""
for i, line in enumerate(_content):
ret += '{0}: {1}\n'.format(i, line)
return ret
class KeywordLexer(object):
def __init__(self, keyword):
if keyword == 'curly_brackets':
keyword = '{|}'
self.keyword = keyword
def get_tokens(self, text):
pattern = '(' + self.keyword + ')'
splits = re.split(pattern, text)
if len(splits) == 1:
return [(token.Text, text)]
res = []
for count, s in enumerate(splits):
if count % 2 == 0:
res.append((token.Text, s))
else:
res.append((token.Keyword.Type, s))
# print(res)
return res
class LineLexer(object):
def __init__(self, line_number):
self.line_number = line_number
def get_tokens(self, text):
splits = text.split('\n')
res = []
splits1 = '\n'.join(splits[:self.line_number]) + '\n'
splits2 = splits[self.line_number] + '\n'
splits3 = '\n' + '\n'.join(splits[self.line_number + 1:]) + '\n'
splits = [splits1] + [splits2] + [splits3]
# print(splits)
for count, s in enumerate(splits):
if count == 1:
res.append((token.Keyword.Type, s))
else:
res.append((token.Text, s))
# print(res)
return res
class CustomLatexFormatter(LatexFormatter):
def __init__(self, **kwargs):
LatexFormatter.__init__(self, **kwargs)
def format(self, tokens, outfile):
res = []
for t, s in tokens:
if t == token.Keyword.Type:
import pygments.formatters.latex as la
s = la.escape_tex(s, self.commandprefix)
s = '\color{red}{\huge{' + s + '}}'
t = token.Escape
res.append((t, s))
LatexFormatter.format(self, res, outfile)
class CustomHtmlFormatter(HtmlFormatter):
def __init__(self, **kwargs):
HtmlFormatter.__init__(self, **kwargs)
def format(self, tokens, outfile_final):
# HtmlFormatter.format(self, tokens, outfile_final)
# return
from io import StringIO
outfile = StringIO()
res = []
ss = []
for t, s in tokens:
if t == token.Keyword.Type:
ss.append(s)
s = 'A' * 24
t = token.Text
res.append((t, s))
HtmlFormatter.format(self, res, outfile)
final = outfile.getvalue()
while ss:
s = ss.pop()
final = final.replace(
'A' * 24,
'{0}'.format(
s),
1)
outfile_final.write(final)
def pigment(self,
keyword=None, line_highlight=None,
start=None, end=None,
output=None, line_numbering=None):
_content = self.content.split('\n')
_content = [(c + '\n') for c in _content]
if start is None:
start = 1
if end is None:
end = len(_content)
if output is None:
output = self.default_output
lexer = CppLexer()
if keyword is not None:
lexer = self.KeywordLexer(keyword)
elif line_highlight is not None:
- lexer = self.LineLexer(line_highlight - 1)
+ if line_highlight < start or line_highlight > end:
+ raise RuntimeError('line_highlight out of the sub snippet')
+ line_highlight -= start
+ lexer = self.LineLexer(line_highlight)
if line_numbering is None:
line_numbering = self.default_line_numbering
if output == 'html':
formatter = HtmlFormatter(
full=False, linenos=line_numbering, linenostart=start)
elif output == 'latex':
formatter = LatexFormatter(
full=False, linenos=line_numbering, linenostart=start)
if (keyword is not None) or (line_highlight is not None):
format_class = self.CustomHtmlFormatter
if output == 'latex':
format_class = self.CustomLatexFormatter
formatter = format_class(
full=False, linenos=line_numbering,
linenostart=start)
snip = _content[start - 1:end]
snip = ''.join(snip)
colored_snippet = highlight(snip, lexer, formatter)
if output == 'html':
HTML_TEMPLATE = """
{}
"""
css = formatter.get_style_defs()
colored_snippet = HTML(HTML_TEMPLATE.format(css, colored_snippet))
return colored_snippet
@staticmethod
def getLatexStyleDefs():
return LatexFormatter(full=True).get_style_defs()
################################################################
class SnippetCollection(Snippet):
def __init__(self):
self.functions = dict()
self.func_names = []
def addFunction(self, f_name, code):
if f_name in self.functions:
raise RuntimeError('function already declared')
self.func_names.append(f_name)
self.functions[f_name] = Snippet(code, format_flag=True)
self.compile()
def __getitem__(self, key):
return self.functions[key]
def flatten(self):
global_input = """
#include
#include
#include
#include
""" + '/' * 70 + '\n\n'
for f_name in self.func_names:
global_input += "void {0}(){{\n".format(f_name)
global_input += self.functions[f_name].content
global_input += '\n}\n\n' + '/' * 70 + '\n\n'
global_input += """
int main(int argc, char ** argv){\n"""
for f_name in self.func_names:
global_input += f_name + '();\n'
global_input += '\n}\n'
flat = Snippet(global_input,
format_flag=True)
return flat
def compile(self, **kwargs):
global_snippet = self.flatten()
global_snippet.compile(**kwargs)
def save(self, filename):
global_snippet = self.flatten()
open(filename, 'w').write(global_snippet.content)
def pigment(self, *args):
return self.flatten().pigment(*args)
def last(self):
return self.functions[self.func_names[-1]]
################################################################
# def printMatlab(code):
#
# html_snippet = highlight(
# code, MatlabLexer(),
# HtmlFormatter(full=True, style='colorful',
# classprefix='matlab_'))
#
# display(HTML(html_snippet))
# # return html_snippet
# ## testing
#Snippet.default_output = 'latex'
#Snippet.default_line_numbering = True
#
#snippet = SnippetCollection()
# snippet.addFunction('if_syntax', '''
# int p, q;
# if (p > q) {
# /*
# Statement1;
# Statement2;
# */
# }
#''')
#
#print (snippet['if_syntax'].pigment())
diff --git a/tests/test_platex.py b/tests/test_platex.py
index f7b8a94..b0141c1 100644
--- a/tests/test_platex.py
+++ b/tests/test_platex.py
@@ -1,288 +1,300 @@
#!/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import print_function
import unittest
try:
from PythonLatex.latex_structure import LatexStructure
except:
pass
class pLatexTest(unittest.TestCase):
"Unit tests for pLatex"
def setUp(self, ):
pass
def test_latex_parser_beamer(self):
latex_code = r"""
\documentclass[9pt,xcolor=dvipsnames]{beamer}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage{fancyvrb}
\begin{python}{header}
from Slides.snippet_helper import Snippet, SnippetCollection
Snippet.default_output='latex'
# Snippet.default_line_numbering=True
\end{python}
\py{Snippet.getLatexStyleDefs()}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{class.tex}
\title{Chapter 4. Pointers}
\begin{document}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\maketitle
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\begin{frame}[fragile]{Pointers and the Computer's Memory}
\only<1>{
\py{yop}
}
\only<2>{
}
$p_x$
ROW\_MAJOR
\end{frame}
\end{document}
"""
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
f = open('tmp.tex', 'w')
f.write(latex_code)
f.close()
f = open('tmp_output.tex', 'w')
f.write(output)
f.close()
if latex_code != output:
print(output)
self.assertEqual(latex_code, output)
def test_latex_parser_underscore(self):
latex_code = r'ROW\_MAJOR'
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
def test_latex_parser_nested_begin_end(self):
latex_code = r'''
\begin{toto}tatie
\begin{re}tututoto \end{re} aa\end{toto}
'''
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
def test_latex_parser_begin_end(self):
latex_code = r'\begin{toto}tatie\end{toto}'
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
def test_latex_parser_text(self):
latex_code = r'tatie'
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
def test_latex_parser_comment(self):
latex_code = r'''
% tatie
toto
'''
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
def test_latex_parser_command(self):
latex_code = r' \documentclass[10pt]{article}'
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
def test_latex_parser_nested_begin_command(self):
latex_code = r'''{ yala {toto } \begin{align} tutu
\begin{equation} toto \end{equation}
\end{align} {\titi{ tutu} } }'''
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
def test_latex_env(self):
latex_code = r'''
\begin{toto} tata
\end{toto}
\begin{tata}
toto
\end{tata}
\begin{tutu}
titi
\begin{titi}
\end{titi}
\end{tutu}
'''
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
blk_list = []
def foo(i):
blk_list.append(i.name)
blk_list_correct = ['main',
'toto', 'toto',
'tata', 'tata',
'tutu', 'titi', 'titi', 'tutu',
'main']
tex_struct.pathInBlock(begin_functor=foo, end_functor=foo)
self.assertEqual(blk_list, blk_list_correct)
def test_latex_python_code(self):
latex_code = r'''
\begin{python}{cpp}
snippet = Snippet('code_snippets/hello.cpp')
\end{python}
'''
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
def test_latex_frame_includegraphics_only(self):
latex_code = r'''
\begin{frame}{What is a computer ?}
\includegraphics<2>[width=\textwidth]{figures/computer-components.png}
\end{frame}
\begin{frame}{What is a program ?}
toto
\end{frame}
'''
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
blk_list = []
def foo(i):
blk_list.append(i.name)
tex_struct.pathInBlock(begin_functor=foo, end_functor=foo)
blk_list_correct = ['main',
'frame', 'frame',
'frame', 'frame',
'main']
self.assertEqual(blk_list, blk_list_correct)
def test_latex_item_beamer(self):
latex_code = r'''
\begin{document}
\begin{frame}
\section{Class organization}
\begin{itemize}
\item<1-> Teaching staff: G. Anciaux, A. Nielsen, L. Pegolotti.
\item<2-> Lectures: on Mondays, exercises on Fridays
\item<3-> Follow chapters of the book: \href{http://link.springer.com/book/10.1007/978-1-4471-2736-9}{Guide To Scientific Computing in C++}
\item<4-> Permanent homework: reading next chapter of the book
\item<5-> Moodle (password: PCSC2017): material, forum (at the beginning
\item<6-> Git: material, pdfs, solutions
\item<7-> Evaluation: project realization and oral presentation
\end{itemize}
\end{frame}
\begin{frame}
\end{frame}
\end{document}
'''
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
blk_list = []
def foo(i):
blk_list.append(i.name)
tex_struct.pathInBlock(begin_functor=foo, end_functor=foo)
blk_list_correct = ['main',
'document',
'frame',
'itemize', 'itemize',
'frame',
'frame',
'frame',
'document',
'main']
self.assertEqual(blk_list, blk_list_correct)
def test_latex_python_frame(self):
latex_code = r'''
\begin{python}{header}
b = 100
\end{python}
\begin{document}
\begin{frame}
\end{frame}
\begin{python}{cpp}
a = 10
\end{python}
\begin{frame}[fragile]
\py{str(a)}
\end{frame}
\end{document}
'''
tex_struct = LatexStructure()
tex_struct.parseLatex(latex_code)
output = str(tex_struct)
self.assertEqual(latex_code, output)
blk_list = []
def foo(i):
blk_list.append(i.name)
tex_struct.pathInBlock(begin_functor=foo, end_functor=foo)
blk_list_correct = ['main',
'python', 'python',
'document',
'frame', 'frame',
'python', 'python',
'frame', 'frame',
'document',
'main']
self.assertEqual(blk_list, blk_list_correct)
+
+ def test_latex_python_special_characters(self):
+ latex_code = r'''
+\begin{document}
+Toto is \% \\ \& \# \_
+Tata
+\end{document}
+'''
+ tex_struct = LatexStructure()
+ tex_struct.parseLatex(latex_code)
+ output = str(tex_struct)
+ self.assertEqual(latex_code, output)