diff --git a/Slides/snippet_helper.py b/Slides/snippet_helper.py
index d376cd5..3f75fb9 100644
--- a/Slides/snippet_helper.py
+++ b/Slides/snippet_helper.py
@@ -1,427 +1,486 @@
#!/usr/bin/env python3
import subprocess
import os
import re
+import abc
from pygments import highlight
from pygments.lexers import CppLexer
from pygments.lexers import PythonLexer
-# 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 SlicedSnippet(object):
"""
When you slice a snippet, this object is returned
so that all your operations can apply directly to the snippet if need be.
"""
def __init__(self, snippet, slice):
self.start = slice.start
if slice.start is None:
self.start = 1
self.end = slice.stop-1
self.snippet = snippet
def pigment(self, **kwargs):
kwargs['start'] = self.start
kwargs['end'] = self.end
return self.snippet.pigment(**kwargs)
-class Snippet(object):
+class Snippet(abc.ABC):
"""
Snippet objects manipulate codes.
- At construction it can be formatted using clang-format
- and it can be compiled to check for consistency.
"""
- class CompilationError(Exception):
- """ Compilation error """
- pass
-
default_output = 'html'
default_line_numbering = False
def __getitem__(self, slice):
return SlicedSnippet(self, slice)
def __init__(self, inp, format_flag=False,
- compile_flag=False, include_paths=None,
- language='cpp'):
+ compile_flag=False,
+ working_dir='/tmp'):
+
+ self.working_dir = working_dir
+
try:
self.loadfile(inp)
except Exception:
self.content = inp.strip()
- if include_paths is None:
- self.include_paths = []
- else:
- self.include_paths = [
- os.path.abspath(i) for i in include_paths]
-
if format_flag:
- self.clang_format()
+ self.format()
if compile_flag:
self.compile()
- if language == 'cpp':
- self.lexer = CppLexer()
- elif language == 'python':
- self.lexer = PythonLexer()
- elif language == 'matlab':
- self.lexer = MatlabLexer()
+ # elif language == 'matlab':
+ # self.lexer = MatlabLexer()
def save(self, filename):
"""
Saves the snippet in another file
:param filename: The filename to save the snippet to
"""
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()
+ def getTemporaryFile(self, **kwargs):
+ f = NamedTemporaryFile(dir=self.working_dir, **kwargs)
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()
- if p.returncode:
- raise RuntimeError('Failed to launch clang-format')
- formatted_file = stdout
- self.content = formatted_file.decode('utf-8')
-
- def execute(self, working_dir='/tmp', args='', libraries=''):
- self.compile(working_dir=working_dir)
-
- 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)
- include_paths = self.include_paths + [working_dir]
- include_paths = ['-I {0}'.format(a) for a in include_paths]
- include_paths = ' '.join(include_paths)
-
- p = subprocess.Popen(
- 'g++ -Wall -Wextra {1} {2} -o exec {0}'.format(
- f.name, include_paths, libraries),
- shell=True,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- stdout, stderr = p.communicate()
+ return f
- ret = p.returncode
- if ret != 0:
- return ret, stdout, stderr
+ @abc.abstractmethod
+ def format(self):
+ raise RuntimeError('pure virtual routine')
- p = subprocess.Popen(
- './exec {0}'.format(args), shell=True,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- stdout, stderr = p.communicate()
- ret = p.returncode
-
- os.chdir(previous_dir)
- return ret, stdout, stderr
+ @abc.abstractmethod
+ def execute(self, working_dir='/tmp', args='', libraries=''):
+ raise RuntimeError('pure virtual routine')
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)
- include_paths = self.include_paths + [working_dir]
- include_paths = ['-I {0}'.format(a) for a in include_paths]
- include_paths = ' '.join(include_paths)
- p = subprocess.Popen(
- 'g++ -Wall -Wextra {1} -c {0}'.format(f.name, include_paths),
- 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)
+ raise RuntimeError('pure virtual routine')
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 = []
# print(splits)
# print(token.Keyword.Type)
for count, s in enumerate(splits):
# print(count, s)
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 + '}}'
+ s = r'\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))
# print(res)
HtmlFormatter.format(self, res, outfile)
final = outfile.getvalue()
while ss:
s = ss.pop(0)
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 = self.lexer
if keyword is not None:
lexer = self.KeywordLexer(keyword)
elif line_highlight is not None:
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)
# print(colored_snippet)
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 CPPSnippet(Snippet):
+ """
+ Snippet objects manipulate Cpp codes.
+ At construction it can be formatted using clang-format
+ and it can be compiled to check for consistency.
+ """
+
+ class CompilationError(Exception):
+ """ Compilation error """
+ pass
+
+ def __init__(self, inp, include_paths=None, **kwargs):
+
+ if include_paths is None:
+ self.include_paths = []
+ else:
+ self.include_paths = [
+ os.path.abspath(i) for i in include_paths]
+
+ Snippet.__init__(self, inp, **kwargs)
+
+ self.lexer = CppLexer()
+
+ def format(self):
+ f = self.getTemporaryFile()
+ p = subprocess.Popen(
+ 'clang-format {0}'.format(f.name),
+ shell=True, stdout=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if p.returncode:
+ raise RuntimeError('Failed to launch clang-format')
+ formatted_file = stdout
+ self.content = formatted_file.decode('utf-8')
+
+ def execute(self, working_dir='/tmp', args='', libraries=''):
+ self.compile(working_dir=working_dir)
+ f = self.getTemporaryFile(suffix='.cpp', delete=False)
+ working_dir = os.path.dirname(f.name)
+ # file_dir = os.path.dirname(self.full_filename)
+ previous_dir = os.getcwd()
+ os.chdir(working_dir)
+ include_paths = self.include_paths + [working_dir]
+ include_paths = ['-I {0}'.format(a) for a in include_paths]
+ include_paths = ' '.join(include_paths)
+
+ p = subprocess.Popen(
+ 'g++ -Wall -Wextra {1} {2} -o exec {0}'.format(
+ f.name, include_paths, libraries),
+ shell=True,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+
+ ret = p.returncode
+ if ret != 0:
+ return ret, stdout, stderr
+
+ p = subprocess.Popen(
+ './exec {0}'.format(args), shell=True,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ ret = p.returncode
+
+ os.chdir(previous_dir)
+ return ret, stdout, stderr
+
+ def compile(self):
+ f = self.getTemporaryFile(suffix='.cpp', delete=False)
+
+ working_dir = os.path.dirname(f.name)
+ # file_dir = os.path.dirname(self.full_filename)
+ previous_dir = os.getcwd()
+ os.chdir(self.working_dir)
+ include_paths = self.include_paths + [working_dir]
+ include_paths = ['-I {0}'.format(a) for a in include_paths]
+ include_paths = ' '.join(include_paths)
+ cmd = 'g++ -Wall -Wextra {1} -c {0}'.format(f.name, include_paths)
+ p = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ ret = p.returncode
+ os.chdir(previous_dir)
+ if ret != 0:
+ raise CPPSnippet.CompilationError('compilation failed\n' +
+ cmd + '\n' +
+ stderr.decode('utf-8'))
+
+ os.remove(f.name)
+ o_file = os.path.splitext(f.name)[0] + '.o'
+ os.remove(o_file)
+
+################################################################
+
+
+class PythonSnippet(Snippet):
+ """
+ Snippet objects manipulate Python codes.
+ At construction it can be formatted using autopep8.
+ """
+
+ def __init__(self, inp, **kwargs):
+
+ Snippet.__init__(self, inp, **kwargs)
+
+ self.lexer = PythonLexer()
+
+ def format(self):
+ f = self.getTemporaryFile()
+ p = subprocess.Popen(
+ 'autopep8 {0}'.format(f.name),
+ shell=True, stdout=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if p.returncode:
+ raise RuntimeError('Failed to launch autopep8')
+ formatted_file = stdout
+ self.content = formatted_file.decode('utf-8')
+
+ def execute(self, args=[]):
+ f = self.getTemporaryFile()
+ p = subprocess.Popen(
+ 'python3 {0} {1}'.format(f.name, ' '.join(args)),
+ shell=True, stdout=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if p.returncode:
+ raise RuntimeError('Failed to run snippet')
+ return stdout
+
+
+################################################################
+
+
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_snippet_helper.py b/tests/test_snippet_helper.py
index ea057b9..cbb3f56 100644
--- a/tests/test_snippet_helper.py
+++ b/tests/test_snippet_helper.py
@@ -1,71 +1,71 @@
#!/bin/env python3
# -*- coding: utf-8 -*-
from Slides import snippet_helper as sh
def test_cpp_snippet_from_file():
sh.Snippet.default_line_number = True
- snip = sh.Snippet('tests/snippet.cpp')
+ snip = sh.CPPSnippet('tests/snippet.cpp')
snip.save('snippet2.cpp')
f = open('snippet2.cpp')
snip2 = f.read()
f.close()
if snip.get_content() != snip2:
print('AAA ', snip.get_content())
print('BBB ', snip2)
raise RuntimeError('failed test')
def test_cpp_snippet_format():
sh.Snippet.default_line_number = True
code = """
#include
int main(int argc, char **argv){
return 0;
}
"""
code = code.replace(';\n', ';')
- snip = sh.Snippet(code, format_flag=True)
+ snip = sh.CPPSnippet(code, format_flag=True)
snip.save('snippet2.cpp')
f = open('snippet2.cpp')
snip2 = f.read()
f.close()
assert snip.get_content() == snip2
assert not snip.get_content() == code
def test_cpp_snippet_compile_fail():
sh.Snippet.default_line_number = True
code = """
#include
int main(int argc, char **argv){
return 0
}
"""
code = code.replace(';\n', ';')
try:
- sh.Snippet(code, compile_flag=True)
+ sh.CPPSnippet(code, compile_flag=True)
raise RuntimeError("compilation error not detected")
- except sh.Snippet.CompilationError:
+ except sh.CPPSnippet.CompilationError:
pass
except Exception as e:
raise RuntimeError('wrong exception:' + str(e))
def test_cpp_snippet_compile():
sh.Snippet.default_line_number = True
code = """
#include
int main(int argc, char **argv){
return 0;
}
"""
code = code.replace(';\n', ';')
try:
- sh.Snippet(code, compile_flag=True)
+ sh.CPPSnippet(code, compile_flag=True)
except Exception as e:
raise RuntimeError('should compile: ' + str(e))