add pdf conversion
This commit is contained in:
parent
c85b27f57a
commit
a29dec4c7a
8 changed files with 543 additions and 238 deletions
17
example/example1.clc
Normal file
17
example/example1.clc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[docu [call font [[family "AR PL UMing TW"]] 123]
|
||||||
|
|
||||||
|
[call font [[family "AR PL UMing TW"]] 123]
|
||||||
|
456
|
||||||
|
[call font [[family "AR PL UMing TW"]] "789漢字"]
|
||||||
|
|
||||||
|
\[7 8 9 \] = \[[+ 5 2] 8 9\]
|
||||||
|
|
||||||
|
[call font [[family "AR PL UMing TW"]] 巨集轉換]
|
||||||
|
|
||||||
|
\[def-syntax foo \[\[_ x \] \[* x x\]\]\]
|
||||||
|
|
||||||
|
[def-syntax foo [[_ x][* x x]]]
|
||||||
|
|
||||||
|
8 * 8 = [foo 8]
|
||||||
|
|
||||||
|
]
|
82
playground/a.py
Normal file
82
playground/a.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
#-*-coding:utf-8-*-
|
||||||
|
import sys, re
|
||||||
|
from PyQt4.QtGui import *
|
||||||
|
from PyQt4.QtCore import *
|
||||||
|
|
||||||
|
class MyHighlighter( QSyntaxHighlighter ):
|
||||||
|
|
||||||
|
def __init__( self, parent, theme ):
|
||||||
|
|
||||||
|
QSyntaxHighlighter.__init__( self, parent )
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
self.parenthesis_color = [Qt.red, Qt.green, Qt.blue]
|
||||||
|
|
||||||
|
def textFormat(self, color):
|
||||||
|
init_format = QTextCharFormat()
|
||||||
|
brush = QBrush( color, Qt.SolidPattern )
|
||||||
|
init_format.setForeground( brush )
|
||||||
|
return init_format
|
||||||
|
|
||||||
|
|
||||||
|
def highlightBlock( self, text ):
|
||||||
|
|
||||||
|
''' ( ( ) )
|
||||||
|
paren_level ___0___|__1__|__2_|__1_|__0
|
||||||
|
'''
|
||||||
|
|
||||||
|
paren_level = self.previousBlockState()
|
||||||
|
if paren_level == -1: # 若是沒有上次的狀態,就設為0
|
||||||
|
paren_level = 0
|
||||||
|
paren_size = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
iterator = re.finditer("[()]", text)
|
||||||
|
|
||||||
|
paran_and_offset = [{"paren": match.group(0), "offset": match.start()} for match in iterator]
|
||||||
|
|
||||||
|
print(paran_and_offset)
|
||||||
|
for i in paran_and_offset:
|
||||||
|
if i["paren"] == QString('('):
|
||||||
|
print("paren_level %d" % paren_level)
|
||||||
|
self.setFormat( i["offset"], paren_size , self.textFormat(self.parenthesis_color[paren_level]) )
|
||||||
|
paren_level += 1
|
||||||
|
elif i["paren"] == QString(')'):
|
||||||
|
print(paren_level)
|
||||||
|
paren_level -= 1
|
||||||
|
self.setFormat( i["offset"], paren_size , self.textFormat(self.parenthesis_color[paren_level]) )
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.setCurrentBlockState(paren_level)
|
||||||
|
|
||||||
|
class HighlightingRule():
|
||||||
|
|
||||||
|
def __init__( self, pattern, format ):
|
||||||
|
|
||||||
|
self.pattern = pattern
|
||||||
|
self.format = format
|
||||||
|
|
||||||
|
class TestApp( QMainWindow ):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
QMainWindow.__init__(self)
|
||||||
|
font = QFont()
|
||||||
|
font.setFamily( "Noto Sans Mono" )
|
||||||
|
font.setFixedPitch( True )
|
||||||
|
font.setPointSize( 11 )
|
||||||
|
editor = QTextEdit()
|
||||||
|
editor.setFont( font )
|
||||||
|
highlighter = MyHighlighter( editor, "Classic" )
|
||||||
|
self.setCentralWidget( editor )
|
||||||
|
self.setWindowTitle( "Syntax Highlighter" )
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = QApplication( sys.argv )
|
||||||
|
window = TestApp()
|
||||||
|
window.show()
|
||||||
|
sys.exit( app.exec_() )
|
||||||
|
|
13
setup.py
13
setup.py
|
@ -3,14 +3,17 @@ import pdb # 先 import 今天要介紹的套件
|
||||||
|
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from setuptools import find_packages, setup
|
from setuptools import find_packages, setup
|
||||||
from src.Editor import __about__
|
|
||||||
|
about = {}
|
||||||
|
with open("./src/Editor/__about__.py") as about_info:
|
||||||
|
exec(about_info.read(), about)
|
||||||
|
|
||||||
third_party_files_and_dir = glob('thirdparty/**',recursive=True)
|
third_party_files_and_dir = glob('thirdparty/**',recursive=True)
|
||||||
third_party_files = [x for x in third_party_files_and_dir if not os.path.isdir(x)]
|
third_party_files = [x for x in third_party_files_and_dir if not os.path.isdir(x)]
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="Clochur",
|
name="Clochur",
|
||||||
version=__about__.version_no,
|
version=about['version_no'],
|
||||||
author="Yoxem Chen",
|
author="Yoxem Chen",
|
||||||
author_email="yoxem.tem98@nctu.edu.tw",
|
author_email="yoxem.tem98@nctu.edu.tw",
|
||||||
description='''A S-expression like typesetting language powered by SILE engine
|
description='''A S-expression like typesetting language powered by SILE engine
|
||||||
|
@ -41,7 +44,7 @@ setup(
|
||||||
},
|
},
|
||||||
|
|
||||||
packages=find_packages(where='src'),
|
packages=find_packages(where='src'),
|
||||||
package_dir={'Editor': 'src/Editor'},
|
package_dir={'Editor': 'src/Editor', 'Interpreter': 'src/Interpreter'},
|
||||||
package_data={'Editor': ['*.pdf', '*.qrc',
|
package_data={'Editor': ['*.pdf', '*.qrc',
|
||||||
'../resources/*.svg',
|
'../resources/*.svg',
|
||||||
'../thirdparty/pdfjs/**',
|
'../thirdparty/pdfjs/**',
|
||||||
|
@ -53,3 +56,7 @@ setup(
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
import re
|
import re
|
||||||
from PyQt5.Qsci import QsciLexerCustom, QsciScintilla
|
from PyQt5.Qsci import QsciLexerCustom, QsciScintilla
|
||||||
from PyQt5.QtGui import *
|
from PyQt5.QtGui import *
|
||||||
|
from Parser import Parser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,23 +17,25 @@ class ClochurLexer(QsciLexerCustom):
|
||||||
0: 'Default',
|
0: 'Default',
|
||||||
1: 'Keyword',
|
1: 'Keyword',
|
||||||
2: 'Comment',
|
2: 'Comment',
|
||||||
3: 'String',
|
3: 'Number',
|
||||||
4: 'Rainbow0',
|
4: 'String',
|
||||||
5: 'Rainbow1',
|
5: 'Rainbow0',
|
||||||
6: 'Rainbow2',
|
6: 'Rainbow1',
|
||||||
7: 'Rainbow3',
|
7: 'Rainbow2',
|
||||||
8: 'Rainbow4',
|
8: 'Rainbow3',
|
||||||
9: 'Rainbow5',
|
9: 'Rainbow4',
|
||||||
10: 'Rainbow6',
|
10: 'Rainbow5',
|
||||||
|
11: 'Rainbow6',
|
||||||
}
|
}
|
||||||
|
|
||||||
for (k,v) in self._styles.items():
|
for (k,v) in self._styles.items():
|
||||||
setattr(self, v, k)
|
setattr(self, v, k)
|
||||||
|
|
||||||
self.QUOTES = ['"', "'"]
|
self.QUOTES = ['"']
|
||||||
self.PARENTHESIS = ["[", "]"]
|
self.PARENTHESIS = ["[", "]"]
|
||||||
|
|
||||||
self.PRIMARY = ['define', 'let' , '#t', '#f', 'lambda', '@', 'cond', 'if', 'docu']
|
self.PRIMARY = ['define', 'def-syntax' , 'True', 'False', 'lambda', '-', '+',
|
||||||
|
'*', '/', '>' ,'=','<','>=','<=', 'if', 'docu', 'font', 'font-family']
|
||||||
|
|
||||||
self.split_pattern = re.compile(r'(\s+|\\%|%|\\\[|\\\]|[[]|[]])')
|
self.split_pattern = re.compile(r'(\s+|\\%|%|\\\[|\\\]|[[]|[]])')
|
||||||
|
|
||||||
|
@ -57,7 +61,9 @@ class ClochurLexer(QsciLexerCustom):
|
||||||
elif style == self.Keyword:
|
elif style == self.Keyword:
|
||||||
return QColor("#0000ff")
|
return QColor("#0000ff")
|
||||||
elif style == self.Comment:
|
elif style == self.Comment:
|
||||||
return QColor("#005500")
|
return QColor("#85cf65")
|
||||||
|
elif style == self.Number:
|
||||||
|
return QColor("#00aaff")
|
||||||
elif style == self.String:
|
elif style == self.String:
|
||||||
return QColor("#ce5c00")
|
return QColor("#ce5c00")
|
||||||
elif style == self.Rainbow0:
|
elif style == self.Rainbow0:
|
||||||
|
@ -122,6 +128,7 @@ class ClochurLexer(QsciLexerCustom):
|
||||||
#print(line_utf8_splitted_len_pair)
|
#print(line_utf8_splitted_len_pair)
|
||||||
|
|
||||||
is_comment = False
|
is_comment = False
|
||||||
|
is_string = False
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
if index > 0:
|
if index > 0:
|
||||||
|
@ -129,8 +136,11 @@ class ClochurLexer(QsciLexerCustom):
|
||||||
rainbow_state = SCI(QsciScintilla.SCI_GETLINESTATE, index - 1)
|
rainbow_state = SCI(QsciScintilla.SCI_GETLINESTATE, index - 1)
|
||||||
# print(rainbow_state)
|
# print(rainbow_state)
|
||||||
|
|
||||||
|
tmp_parser = Parser()
|
||||||
|
|
||||||
for item in line_utf8_splitted_len_pair:
|
for item in line_utf8_splitted_len_pair:
|
||||||
|
|
||||||
|
|
||||||
'''comment'''
|
'''comment'''
|
||||||
if item["str"] == "%":
|
if item["str"] == "%":
|
||||||
is_comment = True
|
is_comment = True
|
||||||
|
@ -138,9 +148,25 @@ class ClochurLexer(QsciLexerCustom):
|
||||||
new_state = self.Comment # end of comment
|
new_state = self.Comment # end of comment
|
||||||
elif item["str"] in self.PRIMARY: # keywords
|
elif item["str"] in self.PRIMARY: # keywords
|
||||||
new_state = self.Keyword
|
new_state = self.Keyword
|
||||||
|
|
||||||
|
# number
|
||||||
|
elif re.match(tmp_parser.int_pattern,item["str"]):
|
||||||
|
new_state = self.Number
|
||||||
|
elif re.match(tmp_parser.float_pattern, item["str"]):
|
||||||
|
new_state = self.Number
|
||||||
|
|
||||||
# string
|
# string
|
||||||
elif re.match(r'^["]([^"]|\\\")*["]$' ,item["str"]) or re.match(r"^[']([^']|\\\')*[']$" ,item["str"]):
|
elif re.match(tmp_parser.string_pattern ,item["str"]):
|
||||||
new_state = self.String
|
new_state = self.String
|
||||||
|
elif re.match(r"[\"]([^\"\\]|[\\][\"\n\t]|[\\])*?", item["str"]):
|
||||||
|
is_string = True
|
||||||
|
new_state = self.String
|
||||||
|
elif re.match(r"([^\"\\]|[\\][\"\n\t]|[\\])*?[\"]" ,item["str"]):
|
||||||
|
new_state = self.String
|
||||||
|
is_string = False
|
||||||
|
elif is_string == True:
|
||||||
|
new_state = self.String
|
||||||
|
|
||||||
#parenthesis: rainbow mode
|
#parenthesis: rainbow mode
|
||||||
elif item["str"] == "[":
|
elif item["str"] == "[":
|
||||||
new_state = getattr(self, "Rainbow" + str(rainbow_state))
|
new_state = getattr(self, "Rainbow" + str(rainbow_state))
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
from PyQt5.QtGui import *
|
from PyQt5.QtGui import *
|
||||||
from PyQt5.Qsci import QsciScintilla
|
from PyQt5.Qsci import QsciScintilla
|
||||||
|
|
||||||
from .ClochurLexer import ClochurLexer
|
from ClochurLexer import ClochurLexer
|
||||||
|
|
||||||
class CustomQsciEditor(QsciScintilla):
|
class CustomQsciEditor(QsciScintilla):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
|
|
|
@ -2,147 +2,7 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
from Parser import Parser
|
||||||
class Parser():
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
float_pattern =r"(?P<flo>[+-]?\d+[.]\d+)"
|
|
||||||
bool_pattern = r"(?P<bool>True|False)"
|
|
||||||
int_pattern =r"(?P<int>[+-]?\d+)"
|
|
||||||
symbol_pattern = r"(?P<sym>[_a-zA-Z][-!._0-9a-zA-Z]*)"
|
|
||||||
string_pattern = r"(?P<str>[\"]([^\"\\]|[\][\\\"\n\t])*[\"])"
|
|
||||||
parenthesis_pattern = r"(?P<paren>[[]|[]])"
|
|
||||||
percent_pattern = r"(?P<percent>[%])"
|
|
||||||
space_pattern = r"(?P<space>[ \t]+)"
|
|
||||||
newline_pattern = r"(?P<nl>)\n"
|
|
||||||
inside_docu_pattern = r"(?P<other>([^%\[\]\n\s\\]|[\\][%\[\]]?)+)"
|
|
||||||
|
|
||||||
|
|
||||||
self.total_pattern = re.compile("|".join([float_pattern,bool_pattern,int_pattern,symbol_pattern,
|
|
||||||
string_pattern,parenthesis_pattern,
|
|
||||||
percent_pattern,inside_docu_pattern,space_pattern,newline_pattern]))
|
|
||||||
|
|
||||||
self.clc_sexp = None
|
|
||||||
self.tokenized = None
|
|
||||||
self.parse_tree = None
|
|
||||||
self.index = None
|
|
||||||
|
|
||||||
|
|
||||||
def get_clc_sexp(self, clc):
|
|
||||||
self.clc_sexp = clc
|
|
||||||
self.tokenized = self.remove_comment(self.tokenize(self.clc_sexp))
|
|
||||||
self.parse_tree = self.parse_main(self.tokenized)
|
|
||||||
|
|
||||||
def generate_printable_sexp(self, sexp):
|
|
||||||
if isinstance(sexp, list):
|
|
||||||
result = "["
|
|
||||||
for i in sexp:
|
|
||||||
result += (self.generate_printable_sexp(i) + " ")
|
|
||||||
result += "]"
|
|
||||||
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
if sexp["type"] == "str":
|
|
||||||
result = sexp["token"].replace("\\", "\\\\")
|
|
||||||
result = "\""+ result[1:-1].replace("\"", "\\\"") + "\""
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
return str(sexp["token"])
|
|
||||||
|
|
||||||
def tokenize(self, clc):
|
|
||||||
line_no = 1
|
|
||||||
column = 0
|
|
||||||
column_offset = 0
|
|
||||||
find_iterator = re.finditer(self.total_pattern, self.clc_sexp)
|
|
||||||
result = []
|
|
||||||
for i in find_iterator:
|
|
||||||
column = i.start() - column_offset
|
|
||||||
|
|
||||||
if i.group(0) == '\n':
|
|
||||||
item = {"token" : i.group(0), "line": line_no, "col" : column, "type": i.lastgroup}
|
|
||||||
line_no += 1
|
|
||||||
column_offset = i.end()
|
|
||||||
else:
|
|
||||||
item = {"token" : i.group(0), "line": line_no, "col" : column, "type": i.lastgroup}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
result.append(item)
|
|
||||||
|
|
||||||
return result
|
|
||||||
def remove_comment(self, series):
|
|
||||||
result = []
|
|
||||||
is_comment_token = False
|
|
||||||
for i in series:
|
|
||||||
if i["token"] == "%":
|
|
||||||
is_comment_token = True
|
|
||||||
elif i["token"] == "\n":
|
|
||||||
if is_comment_token == True:
|
|
||||||
is_comment_token = False
|
|
||||||
else:
|
|
||||||
result.append(i)
|
|
||||||
elif is_comment_token == True:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
result.append(i)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def move_forward(self):
|
|
||||||
self.index += 1
|
|
||||||
|
|
||||||
def parse_main(self, series):
|
|
||||||
self.index = 0
|
|
||||||
|
|
||||||
processed_series = [{"token": "[", "line": None, "col": None, "type": None}] + series + \
|
|
||||||
[{"token": "]", "line": None, "col": None, "type": None}]
|
|
||||||
result = self.parse(processed_series)
|
|
||||||
|
|
||||||
if self.index < len(processed_series):
|
|
||||||
raise Exception("the parenthesis ] is not balanced.")
|
|
||||||
else:
|
|
||||||
return result
|
|
||||||
|
|
||||||
def atom(self, series):
|
|
||||||
result = series[self.index]
|
|
||||||
if result["type"] == "int":
|
|
||||||
result["token"] = int(result["token"])
|
|
||||||
elif result["type"] == "flo":
|
|
||||||
result["token"] = float(result["token"])
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
self.move_forward()
|
|
||||||
return result
|
|
||||||
|
|
||||||
def parse(self, series):
|
|
||||||
result = None
|
|
||||||
if series[self.index]["token"] == "[":
|
|
||||||
result = []
|
|
||||||
self.move_forward()
|
|
||||||
try:
|
|
||||||
while series[self.index]["token"] != "]":
|
|
||||||
item = self.parse(series)
|
|
||||||
result.append(item)
|
|
||||||
|
|
||||||
self.move_forward()
|
|
||||||
|
|
||||||
return result
|
|
||||||
except IndexError:
|
|
||||||
raise Exception("the parenthesis [ is not balanced.")
|
|
||||||
|
|
||||||
|
|
||||||
else:
|
|
||||||
result = self.atom(series)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
macro expansion for example:
|
macro expansion for example:
|
||||||
|
@ -152,7 +12,7 @@ the eclipsis (...) shouldn't be seperated from variable.
|
||||||
[[_ x y] [+ x y]]
|
[[_ x y] [+ x y]]
|
||||||
[[_ x y z...] [+ x [foo y z...]]]]'''
|
[[_ x y z...] [+ x [foo y z...]]]]'''
|
||||||
|
|
||||||
class Intepreter:
|
class Interpreter:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
self.macro_env = [dict()] # {"foo": {"before":[_ x y], "after":[+ x y]}, ...}
|
self.macro_env = [dict()] # {"foo": {"before":[_ x y], "after":[+ x y]}, ...}
|
||||||
|
@ -161,6 +21,49 @@ class Intepreter:
|
||||||
self.macro_list = dict()
|
self.macro_list = dict()
|
||||||
self.silexml = ET.Element('sile')
|
self.silexml = ET.Element('sile')
|
||||||
|
|
||||||
|
self.preprocessing_commands = '''[def-syntax docu
|
||||||
|
[[_ x] [SILE[docu-aux x]]]
|
||||||
|
[[_ x y...] [SILE[docu-aux x y...]]]]
|
||||||
|
|
||||||
|
[def-syntax docu-aux
|
||||||
|
[[_ x] [SILE-STRING-ADD![str x]]]
|
||||||
|
[[_ [x...]] [SILE-STRING-ADD![str [x...]]]]
|
||||||
|
[[_ x y...] [begin[docu-aux x] [docu-aux y...]]]]
|
||||||
|
|
||||||
|
[def-syntax font
|
||||||
|
[[_ [para...] inner] [call font [para...] inner]]
|
||||||
|
]
|
||||||
|
|
||||||
|
[def-syntax font-family
|
||||||
|
[[_ font-f text] [font [[family font-f]] text]]
|
||||||
|
]
|
||||||
|
|
||||||
|
[def-syntax font-size
|
||||||
|
[[_ sz text] [font [[size sz]] text]]
|
||||||
|
]
|
||||||
|
|
||||||
|
[script "packages/rules"] % for underline
|
||||||
|
|
||||||
|
[def-syntax underline
|
||||||
|
[[_ text] [call underline text]]
|
||||||
|
]
|
||||||
|
|
||||||
|
[def-syntax bold
|
||||||
|
[[_ text] [font [[weight 900]] text]]
|
||||||
|
]
|
||||||
|
|
||||||
|
[def-syntax italic
|
||||||
|
[[_ text] [font [[style "italic"]] text]]
|
||||||
|
]
|
||||||
|
'''
|
||||||
|
self.prepocess()
|
||||||
|
|
||||||
|
def prepocess(self):
|
||||||
|
tmp_parser = Parser()
|
||||||
|
parse_tree = tmp_parser.get_clc_sexp(self.preprocessing_commands)
|
||||||
|
self.interprete(parse_tree)
|
||||||
|
|
||||||
|
|
||||||
def remove_spaces_and_newlines(self, sexp):
|
def remove_spaces_and_newlines(self, sexp):
|
||||||
is_inside_defstx = False
|
is_inside_defstx = False
|
||||||
return self.remove_spaces_and_newlines_aux(sexp, is_inside_defstx)
|
return self.remove_spaces_and_newlines_aux(sexp, is_inside_defstx)
|
||||||
|
@ -173,7 +76,8 @@ class Intepreter:
|
||||||
if isinstance(sexp[0], dict) and sexp[0]["token"] == "docu" \
|
if isinstance(sexp[0], dict) and sexp[0]["token"] == "docu" \
|
||||||
and is_inside_defstx == False:
|
and is_inside_defstx == False:
|
||||||
result = []
|
result = []
|
||||||
for i in sexp[1:]:
|
# the sexp[1] is a space, so skip it.
|
||||||
|
for i in sexp[2:]:
|
||||||
if isinstance(i, list):
|
if isinstance(i, list):
|
||||||
result.append(self.remove_spaces_and_newlines_aux(i, is_inside_defstx))
|
result.append(self.remove_spaces_and_newlines_aux(i, is_inside_defstx))
|
||||||
elif i["type"] in ["space", "nl"]:
|
elif i["type"] in ["space", "nl"]:
|
||||||
|
@ -190,14 +94,48 @@ class Intepreter:
|
||||||
else:
|
else:
|
||||||
return sexp
|
return sexp
|
||||||
|
|
||||||
|
def destring(self, string):
|
||||||
|
tmp_parser = Parser()
|
||||||
|
string_pattern = tmp_parser.string_pattern
|
||||||
|
if isinstance(string, dict):
|
||||||
|
string = string["token"]
|
||||||
|
if not isinstance(string, str):
|
||||||
|
string = str(string)
|
||||||
|
if re.match(string_pattern, string):
|
||||||
|
# reverse the escape characters
|
||||||
|
print(string)
|
||||||
|
string = re.sub(r'\\"(.+)',r'"\1',string)
|
||||||
|
print(string)
|
||||||
|
return string[1:-1]
|
||||||
|
else:
|
||||||
|
return string
|
||||||
|
|
||||||
|
# \[ => [ ; \] => ] ; \\ => \
|
||||||
|
def remove_escaping_chars(self, sexp):
|
||||||
|
if isinstance(sexp, list):
|
||||||
|
sexp = [self.remove_escaping_chars(x) for x in sexp]
|
||||||
|
elif not sexp["type"] in ["int", "flo"]:
|
||||||
|
sexp_word = sexp["token"]
|
||||||
|
sexp_word = sexp_word.replace("\\[", "[")
|
||||||
|
sexp_word = sexp_word.replace("\\]", "]")
|
||||||
|
sexp_word = sexp_word.replace("\\\\", "\\")
|
||||||
|
sexp["token"] = sexp_word
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return sexp
|
||||||
|
|
||||||
|
|
||||||
def interprete(self, sexps):
|
def interprete(self, sexps):
|
||||||
|
sexps = self.remove_escaping_chars(sexps)
|
||||||
sexps = self.remove_spaces_and_newlines(sexps)
|
sexps = self.remove_spaces_and_newlines(sexps)
|
||||||
|
result = None
|
||||||
|
|
||||||
#environment = [dict()]
|
#environment = [dict()]
|
||||||
for sexp in sexps:
|
for sexp in sexps:
|
||||||
self.interprete_aux(sexp)
|
result = self.interprete_aux(sexp)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def interprete_aux(self, sexp):
|
def interprete_aux(self, sexp):
|
||||||
if isinstance(sexp, dict):
|
if isinstance(sexp, dict):
|
||||||
|
@ -295,7 +233,10 @@ class Intepreter:
|
||||||
raise Exception("Ln %d, Col %d: the argument number of str should be 1" %
|
raise Exception("Ln %d, Col %d: the argument number of str should be 1" %
|
||||||
(sexp[0]["line"], sexp[0]["col"]))
|
(sexp[0]["line"], sexp[0]["col"]))
|
||||||
else:
|
else:
|
||||||
return str(self.interprete_aux(sexp[1]))
|
if isinstance(sexp[1], dict) and (not (sexp[1]["token"] in self.macro_list.keys())):
|
||||||
|
return str(self.destring(sexp[1]["token"]))
|
||||||
|
else:
|
||||||
|
return str(self.destring(self.interprete_aux(sexp[1])))
|
||||||
|
|
||||||
elif sexp[0]["token"] == "str-append":
|
elif sexp[0]["token"] == "str-append":
|
||||||
if len(sexp) != 3:
|
if len(sexp) != 3:
|
||||||
|
@ -311,6 +252,7 @@ class Intepreter:
|
||||||
else:
|
else:
|
||||||
result = self.interprete_aux(sexp[1])
|
result = self.interprete_aux(sexp[1])
|
||||||
print(result)
|
print(result)
|
||||||
|
return ""
|
||||||
elif sexp[0]["token"] == "set!":
|
elif sexp[0]["token"] == "set!":
|
||||||
if sexp[1]["type"] != "sym":
|
if sexp[1]["type"] != "sym":
|
||||||
raise Exception("Ln %d, Col %d: the type of %s should be symbol, not %s" %
|
raise Exception("Ln %d, Col %d: the type of %s should be symbol, not %s" %
|
||||||
|
@ -347,6 +289,8 @@ class Intepreter:
|
||||||
|
|
||||||
self.macro_list[syntax_name] = result_list
|
self.macro_list[syntax_name] = result_list
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
elif sexp[0]["token"] == "begin":
|
elif sexp[0]["token"] == "begin":
|
||||||
if len(sexp) == 1:
|
if len(sexp) == 1:
|
||||||
raise Exception("Ln %d, Col %d: begin should have argument(s)!" %
|
raise Exception("Ln %d, Col %d: begin should have argument(s)!" %
|
||||||
|
@ -362,12 +306,81 @@ class Intepreter:
|
||||||
(sexp[1]["line"], sexp[1]["col"], sexp[1]))
|
(sexp[1]["line"], sexp[1]["col"], sexp[1]))
|
||||||
else:
|
else:
|
||||||
return Lambda(sexp[1], sexp[2], self.env)
|
return Lambda(sexp[1], sexp[2], self.env)
|
||||||
|
# [script "packages/font-fallback"]
|
||||||
|
elif sexp[0]["token"] == "script":
|
||||||
|
if not len(sexp) == 2:
|
||||||
|
raise Exception("Ln %d, Col %d: argument length of script should be 1" %
|
||||||
|
(sexp[0]["line"], sexp[0]["col"]))
|
||||||
|
else:
|
||||||
|
script_xml = ET.Element('script')
|
||||||
|
script_xml.attrib["src"] = self.destring(sexp[1]["token"])
|
||||||
|
self.silexml.append(script_xml)
|
||||||
|
|
||||||
|
elif sexp[0]["token"] == "docu-para":
|
||||||
|
if not len(sexp) == 2:
|
||||||
|
raise Exception("Ln %d, Col %d: argument length of docu-para should be 1" %
|
||||||
|
(sexp[0]["line"], sexp[0]["col"]))
|
||||||
|
attrib_dict = dict()
|
||||||
|
for i in sexp[1]:
|
||||||
|
attrib_name = i[0]["token"]
|
||||||
|
attrib_value = self.destring(i[1]["token"])
|
||||||
|
self.silexml.attrib[attrib_name] = attrib_value
|
||||||
|
|
||||||
|
# [call callee {[[attr1 val1] [attr2 val2] ...]} {inner_val}]
|
||||||
|
elif sexp[0]["token"] == "call":
|
||||||
|
callee = sexp[1]["token"]
|
||||||
|
call_xml = ET.Element(callee)
|
||||||
|
if len(sexp) == 4 or (len(sexp) == 3 and isinstance(sexp[2], list)):
|
||||||
|
for i in sexp[2]:
|
||||||
|
attrib_name = i[0]["token"]
|
||||||
|
attrib_value = self.destring(self.interprete_aux(i[1]))
|
||||||
|
call_xml.attrib[attrib_name] = attrib_value
|
||||||
|
|
||||||
|
if len(sexp) == 4:
|
||||||
|
call_xml.text = self.destring(self.interprete_aux(sexp[3]))
|
||||||
|
|
||||||
|
self.silexml.append(call_xml)
|
||||||
|
return SubXMLElement(call_xml)
|
||||||
|
elif len(sexp) == 3:
|
||||||
|
call_xml.text = self.destring(self.interprete_aux(sexp[2]))
|
||||||
|
self.silexml.append(call_xml)
|
||||||
|
return SubXMLElement(call_xml)
|
||||||
|
elif len(sexp) == 2:
|
||||||
|
self.silexml.append(call_xml)
|
||||||
|
return SubXMLElement(call_xml)
|
||||||
|
else:
|
||||||
|
raise Exception("Line %d, Col. %d, the form of call is mal-formed." % (sexp[0]["line"], sexp[0]["col"]))
|
||||||
|
|
||||||
|
# if it's a sub-xml-element, show the string form of it, or return the input unchanged.
|
||||||
|
# It's recommended to use it only print it in terminal with 'print'
|
||||||
|
elif sexp[0]["token"] == "xml-to-string":
|
||||||
|
if len(sexp) != 2:
|
||||||
|
raise Exception("Line %d, Col. %d, the argument of SHOW-XML-TREE is mal-formed" % (sexp[0]["line"], sexp[0]["col"]))
|
||||||
|
else:
|
||||||
|
res = self.interprete_aux(sexp[1])
|
||||||
|
if isinstance(res, SubXMLElement):
|
||||||
|
return ET.tostring(res.element, encoding='unicode')
|
||||||
|
else:
|
||||||
|
return res
|
||||||
|
|
||||||
|
# append string to <sile>
|
||||||
|
elif sexp[0]["token"] == "SILE-STRING-ADD!":
|
||||||
|
subelements_found = [x for x in self.silexml.iter() if x != self.silexml]
|
||||||
|
if subelements_found:
|
||||||
|
if subelements_found[-1].tail == None:
|
||||||
|
subelements_found[-1].tail = self.interprete_aux(sexp[1])
|
||||||
|
else:
|
||||||
|
subelements_found[-1].tail += self.interprete_aux(sexp[1])
|
||||||
|
else:
|
||||||
|
if self.silexml.text == None:
|
||||||
|
self.silexml.text = self.interprete_aux(sexp[1])
|
||||||
|
else:
|
||||||
|
self.silexml.text += self.interprete_aux(sexp[1])
|
||||||
|
|
||||||
|
|
||||||
# TODO: SILE argument:
|
|
||||||
#
|
|
||||||
# [docu-para [["pagesize" "a4"]]
|
|
||||||
elif sexp[0]["token"] == "SILE":
|
elif sexp[0]["token"] == "SILE":
|
||||||
self.silexml.text = self.interprete_aux(sexp[1])
|
inner = self.interprete_aux(sexp[1])
|
||||||
|
|
||||||
return ET.tostring(self.silexml, encoding="unicode")
|
return ET.tostring(self.silexml, encoding="unicode")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -411,7 +424,7 @@ class Intepreter:
|
||||||
def unify(self, sexp, before_stx, unification):
|
def unify(self, sexp, before_stx, unification):
|
||||||
for i in range(len(before_stx)):
|
for i in range(len(before_stx)):
|
||||||
if isinstance(before_stx[i], list):
|
if isinstance(before_stx[i], list):
|
||||||
unification = unify(sexp[i], before_stx[i], unification)
|
unification = self.unify(sexp[i], before_stx[i], unification)
|
||||||
elif before_stx[i]["token"] in unification.keys():
|
elif before_stx[i]["token"] in unification.keys():
|
||||||
raise Exception("the variable %s is double defined." % before-stx[i])
|
raise Exception("the variable %s is double defined." % before-stx[i])
|
||||||
elif re.match(r".+[.]{3}$", before_stx[i]["token"]):
|
elif re.match(r".+[.]{3}$", before_stx[i]["token"]):
|
||||||
|
@ -449,8 +462,14 @@ class Intepreter:
|
||||||
return after_stx
|
return after_stx
|
||||||
|
|
||||||
|
|
||||||
|
# a sub xml element that is shown as a empty string, but inside it is a xml element
|
||||||
|
class SubXMLElement:
|
||||||
|
def __init__(self, element):
|
||||||
|
self.element = element
|
||||||
|
|
||||||
|
def __str__(init):
|
||||||
|
return ""
|
||||||
|
# closure
|
||||||
class Lambda:
|
class Lambda:
|
||||||
def __init__(self, vars, body, env):
|
def __init__(self, vars, body, env):
|
||||||
for i in vars:
|
for i in vars:
|
||||||
|
@ -461,64 +480,3 @@ class Lambda:
|
||||||
self.vars = [i["token"] for i in vars]
|
self.vars = [i["token"] for i in vars]
|
||||||
self.body = body
|
self.body = body
|
||||||
self.env = env
|
self.env = env
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''test'''
|
|
||||||
a = Parser()
|
|
||||||
text = '''
|
|
||||||
[def-syntax bar
|
|
||||||
[[_ x y] [+ x y]]
|
|
||||||
[[_ x y z...] [+ x [bar y z...]]]]
|
|
||||||
|
|
||||||
%[print[str[bar 156 6546 146514 10 6]]]
|
|
||||||
|
|
||||||
[define fac [lambda [x] [if [= x 1] 1 [* x [fac [- x 1]]]]]]
|
|
||||||
|
|
||||||
%[print [fac 6]]
|
|
||||||
|
|
||||||
[+[- 2 3][* 5.0 6]]
|
|
||||||
[define var1 [+[- 2 3][* 5.0 6]]]
|
|
||||||
[set! var1 [* 10 2]]
|
|
||||||
[define foo [lambda [x y] [begin [+ x y][set! var1 10] 7]]]
|
|
||||||
[foo 12 5]
|
|
||||||
%[print [+ var1 5]]
|
|
||||||
|
|
||||||
[def-syntax docu
|
|
||||||
[[_ x] [SILE[docu_aux x]]]
|
|
||||||
[[_ x y...] [SILE[docu_aux x y...]]]]
|
|
||||||
|
|
||||||
[def-syntax docu_aux
|
|
||||||
[[_ x] [str x]]
|
|
||||||
[[_ [x...]] [str [x...]]]
|
|
||||||
[[_ x y...] [str-append[docu_aux x] [docu_aux y...]]]]
|
|
||||||
|
|
||||||
[print [docu 貓]]
|
|
||||||
[print[docu 我是貓 [+ 12 3],還沒有名字。]]
|
|
||||||
'''
|
|
||||||
|
|
||||||
"""text = '''[[[ 123 1.23 abc "\\123\\\"貓貓貓"] 我是貓,喵\[喵\]貓\%。喵喵%喵
|
|
||||||
]]'''
|
|
||||||
|
|
||||||
% TODO
|
|
||||||
[def-syntax docu
|
|
||||||
[[_ @ para x] [SILE[docu_aux x]]]
|
|
||||||
[[_ @ para x y...] [SILE[docu_aux x y...]]]]
|
|
||||||
|
|
||||||
[def-syntax docu_aux
|
|
||||||
[[_ x] [str x]]
|
|
||||||
[[_ [x...] [str [x...]]]]
|
|
||||||
[[_ x y...] [str-append[docu_aux x] [docu_aux y...]]]]]
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
interp = Intepreter()
|
|
||||||
|
|
||||||
a.get_clc_sexp(text)
|
|
||||||
|
|
||||||
interp.interprete(a.parse_tree)
|
|
||||||
|
|
||||||
#print(a.parse_tree)
|
|
||||||
print(a.generate_printable_sexp(a.parse_tree))
|
|
137
src/Editor/Parser.py
Normal file
137
src/Editor/Parser.py
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import re
|
||||||
|
class Parser():
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
float_pattern =r"(?P<flo>[+-]?\d+[.]\d+)"
|
||||||
|
bool_pattern = r"(?P<bool>True|False)"
|
||||||
|
int_pattern =r"(?P<int>[+-]?\d+)"
|
||||||
|
symbol_pattern = r"(?P<sym>[_a-zA-Z][-!:._0-9a-zA-Z]*)"
|
||||||
|
string_pattern = r"(?P<str>[\"]([^\"\\]|[\\][\\\"\n\t]|[\\])*?[\"])"
|
||||||
|
parenthesis_pattern = r"(?P<paren>[[]|[]])"
|
||||||
|
percent_pattern = r"(?P<percent>[%])"
|
||||||
|
space_pattern = r"(?P<space>[ \t]+)"
|
||||||
|
newline_pattern = r"(?P<nl>)\n"
|
||||||
|
inside_docu_pattern = r"(?P<other>([^%\[\]\n\s\\]|[\\][%\[\]\\]?)+)"
|
||||||
|
|
||||||
|
|
||||||
|
self.total_pattern = re.compile("|".join([float_pattern,bool_pattern,int_pattern,symbol_pattern,
|
||||||
|
string_pattern,parenthesis_pattern,
|
||||||
|
percent_pattern,inside_docu_pattern,space_pattern,newline_pattern]))
|
||||||
|
|
||||||
|
self.clc_sexp = None
|
||||||
|
self.tokenized = None
|
||||||
|
#self.parse_tree = None
|
||||||
|
self.index = None
|
||||||
|
self.string_pattern = string_pattern
|
||||||
|
self.int_pattern = int_pattern
|
||||||
|
self.float_pattern = float_pattern
|
||||||
|
|
||||||
|
|
||||||
|
def get_clc_sexp(self, clc):
|
||||||
|
self.clc_sexp = clc
|
||||||
|
self.tokenized = self.remove_comment(self.tokenize(self.clc_sexp))
|
||||||
|
parse_tree = self.parse_main(self.tokenized)
|
||||||
|
return parse_tree
|
||||||
|
|
||||||
|
def generate_printable_sexp(self, sexp):
|
||||||
|
if isinstance(sexp, list):
|
||||||
|
result = "["
|
||||||
|
for i in sexp:
|
||||||
|
result += (self.generate_printable_sexp(i) + " ")
|
||||||
|
result += "]"
|
||||||
|
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
if sexp["type"] == "str":
|
||||||
|
result = sexp["token"].replace("\\", "\\\\")
|
||||||
|
result = "\""+ result[1:-1].replace("\"", "\\\"") + "\""
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
return str(sexp["token"])
|
||||||
|
|
||||||
|
def tokenize(self, clc):
|
||||||
|
line_no = 1
|
||||||
|
column = 0
|
||||||
|
column_offset = 0
|
||||||
|
find_iterator = re.finditer(self.total_pattern, self.clc_sexp)
|
||||||
|
result = []
|
||||||
|
for i in find_iterator:
|
||||||
|
column = i.start() - column_offset
|
||||||
|
|
||||||
|
if i.group(0) == '\n':
|
||||||
|
item = {"token" : i.group(0), "line": line_no, "col" : column, "type": i.lastgroup}
|
||||||
|
line_no += 1
|
||||||
|
column_offset = i.end()
|
||||||
|
else:
|
||||||
|
item = {"token" : i.group(0), "line": line_no, "col" : column, "type": i.lastgroup}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
result.append(item)
|
||||||
|
|
||||||
|
return result
|
||||||
|
def remove_comment(self, series):
|
||||||
|
result = []
|
||||||
|
is_comment_token = False
|
||||||
|
for i in series:
|
||||||
|
if i["token"] == "%":
|
||||||
|
is_comment_token = True
|
||||||
|
elif i["token"] == "\n":
|
||||||
|
if is_comment_token == True:
|
||||||
|
is_comment_token = False
|
||||||
|
else:
|
||||||
|
result.append(i)
|
||||||
|
elif is_comment_token == True:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
result.append(i)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def move_forward(self):
|
||||||
|
self.index += 1
|
||||||
|
|
||||||
|
def parse_main(self, series):
|
||||||
|
self.index = 0
|
||||||
|
|
||||||
|
processed_series = [{"token": "[", "line": None, "col": None, "type": None}] + series + \
|
||||||
|
[{"token": "]", "line": None, "col": None, "type": None}]
|
||||||
|
result = self.parse(processed_series)
|
||||||
|
|
||||||
|
if self.index < len(processed_series):
|
||||||
|
raise Exception("the parenthesis ] is not balanced.")
|
||||||
|
else:
|
||||||
|
return result
|
||||||
|
|
||||||
|
def atom(self, series):
|
||||||
|
result = series[self.index]
|
||||||
|
if result["type"] == "int":
|
||||||
|
result["token"] = int(result["token"])
|
||||||
|
elif result["type"] == "flo":
|
||||||
|
result["token"] = float(result["token"])
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
self.move_forward()
|
||||||
|
return result
|
||||||
|
|
||||||
|
def parse(self, series):
|
||||||
|
result = None
|
||||||
|
if series[self.index]["token"] == "[":
|
||||||
|
result = []
|
||||||
|
self.move_forward()
|
||||||
|
try:
|
||||||
|
while series[self.index]["token"] != "]":
|
||||||
|
item = self.parse(series)
|
||||||
|
result.append(item)
|
||||||
|
|
||||||
|
self.move_forward()
|
||||||
|
|
||||||
|
return result
|
||||||
|
except IndexError:
|
||||||
|
raise Exception("the parenthesis [ is not balanced.")
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
result = self.atom(series)
|
||||||
|
return result
|
|
@ -4,19 +4,26 @@
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
|
||||||
from PyQt5.QtCore import *
|
from PyQt5.QtCore import *
|
||||||
from PyQt5.QtGui import *
|
from PyQt5.QtGui import *
|
||||||
from PyQt5 import QtWebEngineWidgets
|
from PyQt5 import QtWebEngineWidgets
|
||||||
from PyQt5.QtWidgets import *
|
from PyQt5.QtWidgets import *
|
||||||
from PyQt5.Qsci import QsciScintilla
|
from PyQt5.Qsci import QsciScintilla
|
||||||
|
|
||||||
from . import qrc_resources
|
import qrc_resources
|
||||||
from . import FindReplace, CustomQsciEditor
|
|
||||||
|
|
||||||
from . import __about__
|
import __about__
|
||||||
|
import FindReplace
|
||||||
|
from Interpreter import Interpreter, Lambda
|
||||||
|
import CustomQsciEditor
|
||||||
|
from Parser import Parser
|
||||||
|
|
||||||
filename = None
|
|
||||||
|
|
||||||
|
sile_command = 'sile'
|
||||||
|
|
||||||
dirname = os.path.abspath(os.path.dirname(__file__)) #os.path.dirname('__file__')
|
dirname = os.path.abspath(os.path.dirname(__file__)) #os.path.dirname('__file__')
|
||||||
PDFJS = os.path.join(dirname, '../thirdparty/pdfjs/web/viewer.html')
|
PDFJS = os.path.join(dirname, '../thirdparty/pdfjs/web/viewer.html')
|
||||||
|
@ -30,7 +37,9 @@ class PDFJSWidget(QtWebEngineWidgets.QWebEngineView):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(PDFJSWidget, self).__init__()
|
super(PDFJSWidget, self).__init__()
|
||||||
self.load(QUrl.fromUserInput("file://%s?file=file://%s" % (PDFJS, PDF)))
|
self.load(QUrl.fromUserInput("file://%s?file=file://%s" % (PDFJS, PDF)))
|
||||||
print((dirname,PDFJS, PDF))
|
#print((dirname,PDFJS, PDF))
|
||||||
|
def load_path(self, path):
|
||||||
|
self.load(QUrl.fromUserInput("file://%s?file=file://%s" % (PDFJS, path)))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,6 +54,7 @@ class Window(QMainWindow):
|
||||||
|
|
||||||
self.tmp_folder = '/tmp'
|
self.tmp_folder = '/tmp'
|
||||||
self.tmp_file = 'clochur_tmp.json'
|
self.tmp_file = 'clochur_tmp.json'
|
||||||
|
self.tmp_output_file = str(hex(hash(int(time.time()))))[2:] # e1f513545c => e1f513545c.pdf, e1f513545c.s
|
||||||
self.untitled_id = None
|
self.untitled_id = None
|
||||||
|
|
||||||
self.opened_file_dirname = os.path.expanduser("~")
|
self.opened_file_dirname = os.path.expanduser("~")
|
||||||
|
@ -72,6 +82,10 @@ class Window(QMainWindow):
|
||||||
self.save_as_action = QAction(QIcon(":save-as.svg"), "Save as...", self)
|
self.save_as_action = QAction(QIcon(":save-as.svg"), "Save as...", self)
|
||||||
self.save_as_action.triggered.connect(self.save_as_call)
|
self.save_as_action.triggered.connect(self.save_as_call)
|
||||||
|
|
||||||
|
self.save_pdf_action = QAction(QIcon(":pdf.svg"), "Save &PDF", self)
|
||||||
|
self.save_pdf_action.setShortcut('Ctrl+P')
|
||||||
|
self.save_pdf_action.triggered.connect(self.save_pdf_call)
|
||||||
|
|
||||||
self.exit_action = QAction("&Exit", self)
|
self.exit_action = QAction("&Exit", self)
|
||||||
self.exit_action.setShortcut('Ctrl+Q')
|
self.exit_action.setShortcut('Ctrl+Q')
|
||||||
self.exit_action.triggered.connect(self.exit_call)
|
self.exit_action.triggered.connect(self.exit_call)
|
||||||
|
@ -105,7 +119,9 @@ class Window(QMainWindow):
|
||||||
self.select_all_action.triggered.connect(self.select_all_call)
|
self.select_all_action.triggered.connect(self.select_all_call)
|
||||||
|
|
||||||
|
|
||||||
self.convert_action = QAction(QIcon(":convert.svg"), "Con&vert", self)
|
self.convert_action = QAction(QIcon(":convert.svg"), "Conv&ert", self)
|
||||||
|
self.convert_action.setShortcut('Ctrl+E')
|
||||||
|
self.convert_action.triggered.connect(self.convert_call)
|
||||||
|
|
||||||
self.about_action = QAction("&About", self)
|
self.about_action = QAction("&About", self)
|
||||||
self.about_action.triggered.connect(self.about_call)
|
self.about_action.triggered.connect(self.about_call)
|
||||||
|
@ -121,8 +137,14 @@ class Window(QMainWindow):
|
||||||
file_menu = menuBar.addMenu("&File")
|
file_menu = menuBar.addMenu("&File")
|
||||||
file_menu.addAction(self.new_action)
|
file_menu.addAction(self.new_action)
|
||||||
file_menu.addAction(self.open_action)
|
file_menu.addAction(self.open_action)
|
||||||
|
file_menu.addSeparator()
|
||||||
|
|
||||||
|
|
||||||
file_menu.addAction(self.save_action)
|
file_menu.addAction(self.save_action)
|
||||||
file_menu.addAction(self.save_as_action)
|
file_menu.addAction(self.save_as_action)
|
||||||
|
file_menu.addAction(self.save_pdf_action)
|
||||||
|
file_menu.addSeparator()
|
||||||
|
|
||||||
file_menu.addAction(self.exit_action)
|
file_menu.addAction(self.exit_action)
|
||||||
|
|
||||||
edit_menu = menuBar.addMenu("&Edit")
|
edit_menu = menuBar.addMenu("&Edit")
|
||||||
|
@ -207,7 +229,7 @@ class Window(QMainWindow):
|
||||||
self.filename = os.path.basename(file_path[0])
|
self.filename = os.path.basename(file_path[0])
|
||||||
self.opened_file_dirname = os.path.dirname(file_path[0])
|
self.opened_file_dirname = os.path.dirname(file_path[0])
|
||||||
self.file = open(file_path[0], 'w', encoding='utf-8')
|
self.file = open(file_path[0], 'w', encoding='utf-8')
|
||||||
file_content = editor.text()
|
file_content = self.editor.text()
|
||||||
self.file.write(file_content)
|
self.file.write(file_content)
|
||||||
self.file.close()
|
self.file.close()
|
||||||
|
|
||||||
|
@ -218,6 +240,45 @@ class Window(QMainWindow):
|
||||||
self.setWindowTitle("Clochur - %s" % os.path.basename(file_path[0]))
|
self.setWindowTitle("Clochur - %s" % os.path.basename(file_path[0]))
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def save_pdf_call(self):
|
||||||
|
dest_pdf_path = QFileDialog.getSaveFileName(self, 'Save pdf as...', self.opened_file_dirname, "Porfable document format (*.pdf)")
|
||||||
|
if dest_pdf_path[0] != '':
|
||||||
|
self.convert_call()
|
||||||
|
sile_pdf_path = os.path.join(self.tmp_folder, self.tmp_output_file+".pdf")
|
||||||
|
shutil.copyfile(sile_pdf_path, dest_pdf_path[0])
|
||||||
|
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
def convert_call(self):
|
||||||
|
text = self.editor.text()
|
||||||
|
|
||||||
|
parser = Parser()
|
||||||
|
try:
|
||||||
|
parse_tree = parser.get_clc_sexp(text)
|
||||||
|
intepreter = Interpreter()
|
||||||
|
result = intepreter.interprete(parse_tree)
|
||||||
|
|
||||||
|
sile_xml_path = os.path.join(self.tmp_folder, self.tmp_output_file+".xml")
|
||||||
|
sile_pdf_path = os.path.join(self.tmp_folder, self.tmp_output_file+".pdf")
|
||||||
|
|
||||||
|
with open(sile_xml_path, "w") as xml:
|
||||||
|
xml.write(result)
|
||||||
|
xml.close()
|
||||||
|
|
||||||
|
subprocess.run([sile_command, sile_xml_path])
|
||||||
|
pdf_js_webviewer_list = self.findChildren(QtWebEngineWidgets.QWebEngineView)
|
||||||
|
pdf_js_webviewer = pdf_js_webviewer_list[-1]
|
||||||
|
pdf_js_webviewer.load_path(sile_pdf_path)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_message = QErrorMessage()
|
||||||
|
error_message.showMessage(str(e))
|
||||||
|
error_message.exec_()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def exit_call(self):
|
def exit_call(self):
|
||||||
|
|
||||||
#reply = QMessageBox.question(self,'','Do You want to save this file? The text has been modified', QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.No)
|
#reply = QMessageBox.question(self,'','Do You want to save this file? The text has been modified', QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel, QMessageBox.No)
|
||||||
|
@ -235,12 +296,14 @@ class Window(QMainWindow):
|
||||||
|
|
||||||
elif reply == QMessageBox.No:
|
elif reply == QMessageBox.No:
|
||||||
self.removing_untitled_id()
|
self.removing_untitled_id()
|
||||||
|
self.remove_tmp_outputs()
|
||||||
app.exit()
|
app.exit()
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.removing_untitled_id()
|
self.removing_untitled_id()
|
||||||
|
self.remove_tmp_outputs()
|
||||||
app.exit()
|
app.exit()
|
||||||
|
|
||||||
def undo_call(self):
|
def undo_call(self):
|
||||||
|
@ -259,7 +322,7 @@ class Window(QMainWindow):
|
||||||
self.editor.cut()
|
self.editor.cut()
|
||||||
|
|
||||||
def find_and_replace_call(self):
|
def find_and_replace_call(self):
|
||||||
print(FindReplace)
|
#print(FindReplace)
|
||||||
find_replace_dialog = FindReplace.FindReplace(self)
|
find_replace_dialog = FindReplace.FindReplace(self)
|
||||||
type(find_replace_dialog)
|
type(find_replace_dialog)
|
||||||
find_replace_dialog.exec_()
|
find_replace_dialog.exec_()
|
||||||
|
@ -280,8 +343,11 @@ class Window(QMainWindow):
|
||||||
|
|
||||||
editToolBar.addAction(self.new_action)
|
editToolBar.addAction(self.new_action)
|
||||||
editToolBar.addAction(self.open_action)
|
editToolBar.addAction(self.open_action)
|
||||||
|
tool_bar_separator = editToolBar.addAction('|')
|
||||||
editToolBar.addAction(self.save_action)
|
editToolBar.addAction(self.save_action)
|
||||||
editToolBar.addAction(self.save_as_action)
|
editToolBar.addAction(self.save_as_action)
|
||||||
|
editToolBar.addAction(self.save_pdf_action)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
tool_bar_separator = editToolBar.addAction('|')
|
tool_bar_separator = editToolBar.addAction('|')
|
||||||
|
@ -358,6 +424,16 @@ class Window(QMainWindow):
|
||||||
|
|
||||||
return "Untitled %d" % self.untitled_id
|
return "Untitled %d" % self.untitled_id
|
||||||
|
|
||||||
|
def remove_tmp_outputs(self):
|
||||||
|
|
||||||
|
sile_xml_path = os.path.join(self.tmp_folder, self.tmp_output_file+".xml")
|
||||||
|
sile_pdf_path = os.path.join(self.tmp_folder, self.tmp_output_file+".pdf")
|
||||||
|
if os.path.isfile(sile_xml_path):
|
||||||
|
os.remove(sile_xml_path)
|
||||||
|
|
||||||
|
if os.path.isfile(sile_pdf_path):
|
||||||
|
os.remove(sile_pdf_path)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -407,3 +483,5 @@ def entry_point():
|
||||||
window.show()
|
window.show()
|
||||||
|
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
|
entry_point()
|
||||||
|
|
Loading…
Reference in a new issue