add pdf conversion

This commit is contained in:
Tan, Kian-ting 2021-06-19 17:29:18 +08:00
parent c85b27f57a
commit a29dec4c7a
8 changed files with 543 additions and 238 deletions

17
example/example1.clc Normal file
View 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
View 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_() )

View file

@ -3,14 +3,17 @@ import pdb # 先 import 今天要介紹的套件
from glob import glob
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 = [x for x in third_party_files_and_dir if not os.path.isdir(x)]
setup(
name="Clochur",
version=__about__.version_no,
version=about['version_no'],
author="Yoxem Chen",
author_email="yoxem.tem98@nctu.edu.tw",
description='''A S-expression like typesetting language powered by SILE engine
@ -41,7 +44,7 @@ setup(
},
packages=find_packages(where='src'),
package_dir={'Editor': 'src/Editor'},
package_dir={'Editor': 'src/Editor', 'Interpreter': 'src/Interpreter'},
package_data={'Editor': ['*.pdf', '*.qrc',
'../resources/*.svg',
'../thirdparty/pdfjs/**',
@ -53,3 +56,7 @@ setup(
)

View file

@ -4,6 +4,8 @@
import re
from PyQt5.Qsci import QsciLexerCustom, QsciScintilla
from PyQt5.QtGui import *
from Parser import Parser
@ -15,23 +17,25 @@ class ClochurLexer(QsciLexerCustom):
0: 'Default',
1: 'Keyword',
2: 'Comment',
3: 'String',
4: 'Rainbow0',
5: 'Rainbow1',
6: 'Rainbow2',
7: 'Rainbow3',
8: 'Rainbow4',
9: 'Rainbow5',
10: 'Rainbow6',
3: 'Number',
4: 'String',
5: 'Rainbow0',
6: 'Rainbow1',
7: 'Rainbow2',
8: 'Rainbow3',
9: 'Rainbow4',
10: 'Rainbow5',
11: 'Rainbow6',
}
for (k,v) in self._styles.items():
setattr(self, v, k)
self.QUOTES = ['"', "'"]
self.QUOTES = ['"']
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+|\\%|%|\\\[|\\\]|[[]|[]])')
@ -57,7 +61,9 @@ class ClochurLexer(QsciLexerCustom):
elif style == self.Keyword:
return QColor("#0000ff")
elif style == self.Comment:
return QColor("#005500")
return QColor("#85cf65")
elif style == self.Number:
return QColor("#00aaff")
elif style == self.String:
return QColor("#ce5c00")
elif style == self.Rainbow0:
@ -122,6 +128,7 @@ class ClochurLexer(QsciLexerCustom):
#print(line_utf8_splitted_len_pair)
is_comment = False
is_string = False
i = 0
if index > 0:
@ -129,8 +136,11 @@ class ClochurLexer(QsciLexerCustom):
rainbow_state = SCI(QsciScintilla.SCI_GETLINESTATE, index - 1)
# print(rainbow_state)
tmp_parser = Parser()
for item in line_utf8_splitted_len_pair:
'''comment'''
if item["str"] == "%":
is_comment = True
@ -138,9 +148,25 @@ class ClochurLexer(QsciLexerCustom):
new_state = self.Comment # end of comment
elif item["str"] in self.PRIMARY: # keywords
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
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
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
elif item["str"] == "[":
new_state = getattr(self, "Rainbow" + str(rainbow_state))

View file

@ -4,7 +4,7 @@
from PyQt5.QtGui import *
from PyQt5.Qsci import QsciScintilla
from .ClochurLexer import ClochurLexer
from ClochurLexer import ClochurLexer
class CustomQsciEditor(QsciScintilla):
def __init__(self, parent=None):

View file

@ -2,147 +2,7 @@
import re
import xml.etree.ElementTree as ET
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
from Parser import Parser
'''
macro expansion for example:
@ -152,7 +12,7 @@ the eclipsis (...) shouldn't be seperated from variable.
[[_ x y] [+ x y]]
[[_ x y z...] [+ x [foo y z...]]]]'''
class Intepreter:
class Interpreter:
def __init__(self):
self.macro_env = [dict()] # {"foo": {"before":[_ x y], "after":[+ x y]}, ...}
@ -161,6 +21,49 @@ class Intepreter:
self.macro_list = dict()
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):
is_inside_defstx = False
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" \
and is_inside_defstx == False:
result = []
for i in sexp[1:]:
# the sexp[1] is a space, so skip it.
for i in sexp[2:]:
if isinstance(i, list):
result.append(self.remove_spaces_and_newlines_aux(i, is_inside_defstx))
elif i["type"] in ["space", "nl"]:
@ -190,14 +94,48 @@ class Intepreter:
else:
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):
sexps = self.remove_escaping_chars(sexps)
sexps = self.remove_spaces_and_newlines(sexps)
result = None
#environment = [dict()]
for sexp in sexps:
self.interprete_aux(sexp)
result = self.interprete_aux(sexp)
return result
def interprete_aux(self, sexp):
if isinstance(sexp, dict):
@ -295,7 +233,10 @@ class Intepreter:
raise Exception("Ln %d, Col %d: the argument number of str should be 1" %
(sexp[0]["line"], sexp[0]["col"]))
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":
if len(sexp) != 3:
@ -311,6 +252,7 @@ class Intepreter:
else:
result = self.interprete_aux(sexp[1])
print(result)
return ""
elif sexp[0]["token"] == "set!":
if sexp[1]["type"] != "sym":
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
return ""
elif sexp[0]["token"] == "begin":
if len(sexp) == 1:
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]))
else:
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
# TODO: SILE argument:
#
# [docu-para [["pagesize" "a4"]]
# [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])
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")
else:
@ -411,7 +424,7 @@ class Intepreter:
def unify(self, sexp, before_stx, unification):
for i in range(len(before_stx)):
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():
raise Exception("the variable %s is double defined." % before-stx[i])
elif re.match(r".+[.]{3}$", before_stx[i]["token"]):
@ -449,8 +462,14 @@ class Intepreter:
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:
def __init__(self, vars, body, env):
for i in vars:
@ -461,64 +480,3 @@ class Lambda:
self.vars = [i["token"] for i in vars]
self.body = body
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
View 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

View file

@ -4,19 +4,26 @@
import json
import os
import sys
import time
import subprocess
import shutil
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5 import QtWebEngineWidgets
from PyQt5.QtWidgets import *
from PyQt5.Qsci import QsciScintilla
from . import qrc_resources
from . import FindReplace, CustomQsciEditor
import qrc_resources
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__')
PDFJS = os.path.join(dirname, '../thirdparty/pdfjs/web/viewer.html')
@ -30,7 +37,9 @@ class PDFJSWidget(QtWebEngineWidgets.QWebEngineView):
def __init__(self):
super(PDFJSWidget, self).__init__()
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_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.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.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.setShortcut('Ctrl+Q')
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.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.triggered.connect(self.about_call)
@ -121,8 +137,14 @@ class Window(QMainWindow):
file_menu = menuBar.addMenu("&File")
file_menu.addAction(self.new_action)
file_menu.addAction(self.open_action)
file_menu.addSeparator()
file_menu.addAction(self.save_action)
file_menu.addAction(self.save_as_action)
file_menu.addAction(self.save_pdf_action)
file_menu.addSeparator()
file_menu.addAction(self.exit_action)
edit_menu = menuBar.addMenu("&Edit")
@ -207,7 +229,7 @@ class Window(QMainWindow):
self.filename = os.path.basename(file_path[0])
self.opened_file_dirname = os.path.dirname(file_path[0])
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.close()
@ -218,6 +240,45 @@ class Window(QMainWindow):
self.setWindowTitle("Clochur - %s" % os.path.basename(file_path[0]))
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):
#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:
self.removing_untitled_id()
self.remove_tmp_outputs()
app.exit()
else:
pass
else:
self.removing_untitled_id()
self.remove_tmp_outputs()
app.exit()
def undo_call(self):
@ -259,7 +322,7 @@ class Window(QMainWindow):
self.editor.cut()
def find_and_replace_call(self):
print(FindReplace)
#print(FindReplace)
find_replace_dialog = FindReplace.FindReplace(self)
type(find_replace_dialog)
find_replace_dialog.exec_()
@ -280,8 +343,11 @@ class Window(QMainWindow):
editToolBar.addAction(self.new_action)
editToolBar.addAction(self.open_action)
tool_bar_separator = editToolBar.addAction('|')
editToolBar.addAction(self.save_action)
editToolBar.addAction(self.save_as_action)
editToolBar.addAction(self.save_pdf_action)
tool_bar_separator = editToolBar.addAction('|')
@ -357,6 +423,16 @@ class Window(QMainWindow):
self.untitled_id = i
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()
sys.exit(app.exec_())
entry_point()