modify something
This commit is contained in:
parent
cb2cd421d3
commit
c04efe041d
12 changed files with 308 additions and 76 deletions
|
@ -3,6 +3,7 @@ A Scheme-like typesetting LISP interpreter and editor that using SILE typesettin
|
||||||
|
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
* Python3
|
||||||
* PyQt5>=5.15
|
* PyQt5>=5.15
|
||||||
* QScintilla>=2.12
|
* QScintilla>=2.12
|
||||||
* SILE>=0.10
|
* SILE>=0.10
|
||||||
|
@ -20,10 +21,10 @@ see `src/example.pdf`
|
||||||
|
|
||||||
To make a wheel package, run the following command in the root folder:
|
To make a wheel package, run the following command in the root folder:
|
||||||
|
|
||||||
`python setup.py bdist_wheel`
|
`python3 setup.py bdist_wheel`
|
||||||
|
|
||||||
and then:
|
and then:
|
||||||
|
|
||||||
`cd dist; ls`
|
`cd dist; ls`
|
||||||
|
|
||||||
`Clochur-x.y.z-py3-none-any.whl` will in it.
|
`Clochur-x.y.z-py3-none-any.whl` will be in it.
|
||||||
|
|
|
@ -2,6 +2,28 @@
|
||||||
[script "packages/font-fallback"]
|
[script "packages/font-fallback"]
|
||||||
[script "packages/grid"]
|
[script "packages/grid"]
|
||||||
|
|
||||||
|
[define section-var 1]
|
||||||
|
[define sub-section-var 1]
|
||||||
|
[define section-title ""]
|
||||||
|
|
||||||
|
% custom macro
|
||||||
|
[def-syntax section
|
||||||
|
[[_ x][begin
|
||||||
|
[set! section-title [str-append [str-append [str section-var] [str ". " ]] [str x]]]
|
||||||
|
[font [[size "20pt"][weight "800"]] section-title]
|
||||||
|
[set! section-var [+ section-var 1]]
|
||||||
|
[set! sub-section-var 1]
|
||||||
|
]]]
|
||||||
|
|
||||||
|
% custom macro
|
||||||
|
|
||||||
|
|
||||||
|
[def-syntax subsection
|
||||||
|
[[_ x][begin
|
||||||
|
[set! section-title [str-append [str-append [str sub-section-var] [str ". " ]] [str x]]]
|
||||||
|
[font [[size "16pt"][weight "600"]] section-title]
|
||||||
|
[set! sub-section-var [+ sub-section-var 1]]
|
||||||
|
]]]
|
||||||
|
|
||||||
|
|
||||||
[docu-para [[class "book"][papersize "b5"]]]
|
[docu-para [[class "book"][papersize "b5"]]]
|
||||||
|
@ -12,9 +34,11 @@
|
||||||
|
|
||||||
[font-size "20pt" "Welcome to use"] [font [[family "sligeach_orig"][size "20pt"]] "Clóċur"][font-size "20pt" ", a toy editor, toy intepreter and a toy typesetting-engine frontend."]
|
[font-size "20pt" "Welcome to use"] [font [[family "sligeach_orig"][size "20pt"]] "Clóċur"][font-size "20pt" ", a toy editor, toy intepreter and a toy typesetting-engine frontend."]
|
||||||
|
|
||||||
[call section "What is Clochur?"]
|
[section "What is Clochur?"]
|
||||||
|
|
||||||
Clochur, or printed as [str "\""][font-family "sligeach_orig" "Clóċur" ][str "\""] in Irish language (Clóċur as Roman type, which means [q]typesetting[q]), is a toy-lisp typesetting language with a intepreter written in Python, and with a simple Editor written in PyQt and Qscintilla.
|
|
||||||
|
|
||||||
|
Clochur, or printed as "\""[font-family "sligeach_orig" "Clóċur"]"\"" in Irish language ([italic "\"CLOW-kur\""] Clóċur as Roman type, which means [q]typesetting[q]), is a toy-lisp typesetting language with a intepreter written in Python 3, and with a simple Editor written in PyQt5 and QScintilla.
|
||||||
|
|
||||||
It generate a XML that is readable for SILE, which is a typesetting engine written in Lua,
|
It generate a XML that is readable for SILE, which is a typesetting engine written in Lua,
|
||||||
and it generate PDF with SILE.
|
and it generate PDF with SILE.
|
||||||
|
@ -28,4 +52,23 @@ The functions that it has (although may be buggy or needed to be tested) is:
|
||||||
- count basic arithmetic expression.
|
- count basic arithmetic expression.
|
||||||
|
|
||||||
[str "- lambda function"], function definition.
|
[str "- lambda function"], function definition.
|
||||||
]
|
|
||||||
|
[section "Simple manual"]
|
||||||
|
|
||||||
|
[subsection "Macro"]
|
||||||
|
|
||||||
|
Using 'def-syntax', you can add your macro to sile. the example is a "custom-section" macro:
|
||||||
|
|
||||||
|
[font-family "Noto Sans Mono CJK TC"
|
||||||
|
% custom macro
|
||||||
|
"[def-syntax \"section\"
|
||||||
|
|
||||||
|
[[_ x][begin] ]
|
||||||
|
|
||||||
|
[set! section-title [str-append [str-append [str [str \"section-var\"]] [str \". \" ]] [str x]]]
|
||||||
|
|
||||||
|
[font [[size \"20pt\"][weight \"800\"]] [str \"section-title\"]]
|
||||||
|
|
||||||
|
[set! [str \"section-var\"] [+ [str \"section-var\"] 1]]
|
||||||
|
|
||||||
|
]]]"
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
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 Editor.Parser import Parser
|
from Clochur.Parser import Parser
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,8 +34,16 @@ class ClochurLexer(QsciLexerCustom):
|
||||||
self.QUOTES = ['"']
|
self.QUOTES = ['"']
|
||||||
self.PARENTHESIS = ["[", "]"]
|
self.PARENTHESIS = ["[", "]"]
|
||||||
|
|
||||||
self.PRIMARY = ['define', 'def-syntax' , 'True', 'False', 'lambda', '-', '+',
|
self.parent = parent
|
||||||
'*', '/', '>' ,'=','<','>=','<=', 'if', 'docu', 'font', 'font-family']
|
|
||||||
|
|
||||||
|
macro_list = ['docu', 'font', 'font-family','font-size','underline','bold','italic']
|
||||||
|
boolean_list = ['True', 'False']
|
||||||
|
operator_list = [ '-', '+', '*', '/', '>' ,'=','<','>=','<=']
|
||||||
|
# SILE and SILE-STRING-ADD! is internal, so they're not added.
|
||||||
|
function_list = ['if', 'docu', 'docu-para', 'script', 'call','xml-to-string', 'begin',
|
||||||
|
'str','str-append','set!','print', 'define', 'def-syntax', 'lambda', 'eval']
|
||||||
|
self.PRIMARY = macro_list + boolean_list + operator_list + function_list
|
||||||
|
|
||||||
self.split_pattern = re.compile(r'(\s+|\\%|%|\\\[|\\\]|[[]|[]])')
|
self.split_pattern = re.compile(r'(\s+|\\%|%|\\\[|\\\]|[[]|[]])')
|
||||||
|
|
||||||
|
@ -106,7 +114,7 @@ class ClochurLexer(QsciLexerCustom):
|
||||||
|
|
||||||
|
|
||||||
self.startStyling(start, 0x1f)
|
self.startStyling(start, 0x1f)
|
||||||
rainbow_state = 0
|
rainbow_state = 0 # 0~6 = Rainbowmode ; 0~6 + 10 (i.e. 10~16) = Rainbowmode with string
|
||||||
|
|
||||||
index = SCI(QsciScintilla.SCI_LINEFROMPOSITION, start)
|
index = SCI(QsciScintilla.SCI_LINEFROMPOSITION, start)
|
||||||
|
|
||||||
|
@ -128,7 +136,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
|
next_is_defined_var = False
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
if index > 0:
|
if index > 0:
|
||||||
|
@ -140,12 +148,37 @@ class ClochurLexer(QsciLexerCustom):
|
||||||
|
|
||||||
for item in line_utf8_splitted_len_pair:
|
for item in line_utf8_splitted_len_pair:
|
||||||
|
|
||||||
|
## add to complete list
|
||||||
|
#if item["str"] in [ "define", "def-syntax"]:
|
||||||
|
# next_is_defined_var = True
|
||||||
|
#elif next_is_defined_var == True and not (item["str"] in ["[","]"]):
|
||||||
|
# print(next_is_defined_var,item["str"])
|
||||||
|
# self.parent.append_autocompletion_item(item["str"])
|
||||||
|
# next_is_defined_var = False
|
||||||
|
#else:
|
||||||
|
# pass
|
||||||
|
|
||||||
'''comment'''
|
'''comment'''
|
||||||
if item["str"] == "%":
|
if item["str"] == "%":
|
||||||
is_comment = True
|
is_comment = True
|
||||||
if is_comment == True:
|
if is_comment == True:
|
||||||
new_state = self.Comment # end of comment
|
new_state = self.Comment # end of comment
|
||||||
|
|
||||||
|
# string
|
||||||
|
elif re.match(tmp_parser.string_pattern ,item["str"]):
|
||||||
|
new_state = self.String
|
||||||
|
elif re.match(r"[\"]([^\"\\]|[\\][\"nt]|[\\][\\])+?", item["str"]):
|
||||||
|
rainbow_state += 10
|
||||||
|
new_state = self.String
|
||||||
|
elif (re.match(r"([^\"\\]|[\\][\"nt]|[\\][\\])+?[\"]" ,item["str"]) or re.match(r'["]' ,item["str"])):
|
||||||
|
new_state = self.String
|
||||||
|
rainbow_state -= 10
|
||||||
|
|
||||||
|
elif item["str"] == "]" and rainbow_state >= 10:
|
||||||
|
new_state = self.String
|
||||||
|
elif rainbow_state >= 10:
|
||||||
|
new_state = self.String
|
||||||
|
|
||||||
elif item["str"] in self.PRIMARY: # keywords
|
elif item["str"] in self.PRIMARY: # keywords
|
||||||
new_state = self.Keyword
|
new_state = self.Keyword
|
||||||
|
|
||||||
|
@ -154,26 +187,25 @@ class ClochurLexer(QsciLexerCustom):
|
||||||
new_state = self.Number
|
new_state = self.Number
|
||||||
elif re.match(tmp_parser.float_pattern, item["str"]):
|
elif re.match(tmp_parser.float_pattern, item["str"]):
|
||||||
new_state = self.Number
|
new_state = self.Number
|
||||||
|
|
||||||
# string
|
|
||||||
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
|
#parenthesis: rainbow mode
|
||||||
elif item["str"] == "[":
|
elif item["str"] == "[":
|
||||||
new_state = getattr(self, "Rainbow" + str(rainbow_state))
|
if rainbow_state >= 10:
|
||||||
rainbow_state = (rainbow_state + 1) % 7
|
new_state = self.String
|
||||||
|
elif rainbow_state < 7:
|
||||||
|
print("rainbow_state" + str(type(rainbow_state)) + str(rainbow_state))
|
||||||
|
new_state = getattr(self, "Rainbow" + str(rainbow_state))
|
||||||
|
rainbow_state = (rainbow_state + 1) % 7
|
||||||
|
else:
|
||||||
|
pass
|
||||||
elif item["str"] == "]":
|
elif item["str"] == "]":
|
||||||
rainbow_state = (rainbow_state - 1) % 7
|
if rainbow_state >= 10:
|
||||||
new_state = getattr(self, "Rainbow" + str(rainbow_state))
|
new_state = self.String
|
||||||
|
elif rainbow_state < 7:
|
||||||
|
rainbow_state = (rainbow_state - 1) % 7
|
||||||
|
new_state = getattr(self, "Rainbow" + str(rainbow_state))
|
||||||
|
else:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
#-*-coding:utf-8-*-
|
#-*-coding:utf-8-*-
|
||||||
|
|
||||||
from PyQt5.QtGui import *
|
from PyQt5.QtGui import *
|
||||||
from PyQt5.Qsci import QsciScintilla
|
from PyQt5.Qsci import QsciScintilla, QsciAPIs
|
||||||
|
|
||||||
from Editor.ClochurLexer import ClochurLexer
|
from Clochur.ClochurLexer import ClochurLexer
|
||||||
|
|
||||||
class CustomQsciEditor(QsciScintilla):
|
class CustomQsciEditor(QsciScintilla):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
|
@ -15,8 +15,7 @@ class CustomQsciEditor(QsciScintilla):
|
||||||
|
|
||||||
self.tab_width = 4
|
self.tab_width = 4
|
||||||
|
|
||||||
lexer = ClochurLexer(self)
|
|
||||||
self.setLexer(lexer)
|
|
||||||
|
|
||||||
# Margin 0 for line numbers
|
# Margin 0 for line numbers
|
||||||
font = QFont()
|
font = QFont()
|
||||||
|
@ -51,6 +50,39 @@ class CustomQsciEditor(QsciScintilla):
|
||||||
self.setTabWidth(4)
|
self.setTabWidth(4)
|
||||||
self.setBackspaceUnindents(True)
|
self.setBackspaceUnindents(True)
|
||||||
|
|
||||||
|
# set auto complete
|
||||||
|
|
||||||
|
#set lexer
|
||||||
|
self.lexer = ClochurLexer(self)
|
||||||
|
self.auto_complete_api = QsciAPIs(self.lexer) # autocomplete function
|
||||||
|
self.setLexer(self.lexer)
|
||||||
|
|
||||||
|
self.setAutoCompletionCaseSensitivity(True)
|
||||||
|
|
||||||
|
self.setAutoCompletionReplaceWord(False)
|
||||||
|
|
||||||
|
# Use the predefined APIs as the source.
|
||||||
|
self.setAutoCompletionSource(QsciScintilla.AcsAll)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# after 1 character, show completetion
|
||||||
|
self.setAutoCompletionThreshold(1)
|
||||||
|
|
||||||
|
# add autocompletion items
|
||||||
|
autocompletions = self.lexer.PRIMARY
|
||||||
|
|
||||||
|
|
||||||
|
for ac in autocompletions:
|
||||||
|
self.auto_complete_api.add(ac)
|
||||||
|
|
||||||
|
# "prepare" the QsciAPIs-object:
|
||||||
|
self.auto_complete_api.prepare()
|
||||||
|
|
||||||
|
def append_autocompletion_item(self, item):
|
||||||
|
self.auto_complete_api.add(item)
|
||||||
|
self.auto_complete_api.prepare()
|
||||||
|
|
||||||
def set_word_wrap(self):
|
def set_word_wrap(self):
|
||||||
self.setWrapMode(QsciScintilla.WrapMode.WrapWord)
|
self.setWrapMode(QsciScintilla.WrapMode.WrapWord)
|
||||||
def set_no_word_wrap(self):
|
def set_no_word_wrap(self):
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from Editor.Parser import Parser
|
import itertools
|
||||||
|
from Clochur.Parser import Parser
|
||||||
|
|
||||||
'''
|
'''
|
||||||
macro expansion for example:
|
macro expansion for example:
|
||||||
|
@ -21,6 +22,11 @@ class Interpreter:
|
||||||
self.macro_list = dict()
|
self.macro_list = dict()
|
||||||
self.silexml = ET.Element('sile')
|
self.silexml = ET.Element('sile')
|
||||||
|
|
||||||
|
#self.editor = None
|
||||||
|
|
||||||
|
#if "editor" in kwargs.keys():
|
||||||
|
# self.editor = kwargs["editor"]
|
||||||
|
|
||||||
self.preprocessing_commands = '''[def-syntax docu
|
self.preprocessing_commands = '''[def-syntax docu
|
||||||
[[_ x] [SILE[docu-aux x]]]
|
[[_ x] [SILE[docu-aux x]]]
|
||||||
[[_ x y...] [SILE[docu-aux x y...]]]]
|
[[_ x y...] [SILE[docu-aux x y...]]]]
|
||||||
|
@ -103,9 +109,11 @@ class Interpreter:
|
||||||
string = str(string)
|
string = str(string)
|
||||||
if re.match(string_pattern, string):
|
if re.match(string_pattern, string):
|
||||||
# reverse the escape characters
|
# reverse the escape characters
|
||||||
print(string)
|
while True:
|
||||||
string = re.sub(r'\\"(.+)',r'"\1',string)
|
string_before = string
|
||||||
print(string)
|
string = re.sub(r'\\"(.+)',r'"\1',string)
|
||||||
|
if string_before == string:
|
||||||
|
break
|
||||||
return string[1:-1]
|
return string[1:-1]
|
||||||
else:
|
else:
|
||||||
return string
|
return string
|
||||||
|
@ -153,10 +161,26 @@ class Interpreter:
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return sexp["token"]
|
return sexp["token"]
|
||||||
|
|
||||||
|
elif isinstance(sexp, Lambda):
|
||||||
|
return sexp
|
||||||
|
|
||||||
|
# lambda apply
|
||||||
|
elif isinstance(sexp[0], Lambda):
|
||||||
|
return self.apply(sexp)
|
||||||
|
|
||||||
|
# count sexp[0] first.
|
||||||
|
elif isinstance(sexp[0], list):
|
||||||
|
new_sexp_0 = self.interprete_aux(sexp[0])
|
||||||
|
new_sexp = [new_sexp_0] + sexp[1:]
|
||||||
|
return self.interprete_aux(new_sexp)
|
||||||
|
|
||||||
|
|
||||||
elif sexp[0]["token"] in ["+", "-", "*", "/", "<", "=", ">", "<=", ">="]:
|
elif sexp[0]["token"] in ["+", "-", "*", "/", "<", "=", ">", "<=", ">="]:
|
||||||
if len(sexp) != 3:
|
if len(sexp) != 3:
|
||||||
|
parser = Parser()
|
||||||
raise Exception("Ln %d, Col %d: the argument length %d of %s is not correct." %
|
raise Exception("Ln %d, Col %d: the argument length %d of %s is not correct." %
|
||||||
(sexp[0]["line"], sexp[0]["col"], len(sexp), a.generate_printable_sexp(sexp)))
|
(sexp[0]["line"], sexp[0]["col"], len(sexp), parser.generate_printable_sexp(sexp)))
|
||||||
else:
|
else:
|
||||||
if sexp[0]["token"] == "+":
|
if sexp[0]["token"] == "+":
|
||||||
return self.interprete_aux(sexp[1]) + self.interprete_aux(sexp[2])
|
return self.interprete_aux(sexp[1]) + self.interprete_aux(sexp[2])
|
||||||
|
@ -203,7 +227,8 @@ class Interpreter:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if not is_found:
|
if not is_found:
|
||||||
raise Exception("The syntax pattern for %s is not found." % a.generate_printable_sexp(sexp))
|
parser = Parser()
|
||||||
|
raise Exception("The syntax pattern for %s is not found." % parser.generate_printable_sexp(sexp))
|
||||||
else:
|
else:
|
||||||
new_sexp = self.macro_expand(syntax_after, unification)
|
new_sexp = self.macro_expand(syntax_after, unification)
|
||||||
|
|
||||||
|
@ -216,8 +241,14 @@ class Interpreter:
|
||||||
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" %
|
||||||
(sexp[1]["line"], sexp[1]["col"], sexp[1], sexp[1]["type"]))
|
(sexp[1]["line"], sexp[1]["col"], sexp[1], sexp[1]["type"]))
|
||||||
else:
|
else:
|
||||||
self.env[-1][sexp[1]["token"]] = self.interprete_aux(sexp[2])
|
defined_var = sexp[1]["token"]
|
||||||
|
self.env[-1][defined_var] = self.interprete_aux(sexp[2])
|
||||||
|
|
||||||
|
#if self.editor != None:
|
||||||
|
# self.editor.append_autocompletion_item(defined_var)
|
||||||
|
return ""
|
||||||
|
|
||||||
elif sexp[0]["token"] == "if":
|
elif sexp[0]["token"] == "if":
|
||||||
if len(sexp) != 4:
|
if len(sexp) != 4:
|
||||||
raise Exception("Ln %d, Col %d: the number of argument of if should be 3." %
|
raise Exception("Ln %d, Col %d: the number of argument of if should be 3." %
|
||||||
|
@ -233,7 +264,12 @@ class Interpreter:
|
||||||
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:
|
||||||
if isinstance(sexp[1], dict) and (not (sexp[1]["token"] in self.macro_list.keys())):
|
vars_frame = [frame.keys() for frame in self.env]
|
||||||
|
vars_list = list(itertools.chain.from_iterable(vars_frame))
|
||||||
|
macro_vars = self.macro_list.keys()
|
||||||
|
if isinstance(sexp[1], dict) and \
|
||||||
|
(not (sexp[1]["token"] in macro_vars)) and \
|
||||||
|
(not (sexp[1]["token"] in vars_list)) :
|
||||||
return str(self.destring(sexp[1]["token"]))
|
return str(self.destring(sexp[1]["token"]))
|
||||||
else:
|
else:
|
||||||
return str(self.destring(self.interprete_aux(sexp[1])))
|
return str(self.destring(self.interprete_aux(sexp[1])))
|
||||||
|
@ -253,6 +289,14 @@ class Interpreter:
|
||||||
result = self.interprete_aux(sexp[1])
|
result = self.interprete_aux(sexp[1])
|
||||||
print(result)
|
print(result)
|
||||||
return ""
|
return ""
|
||||||
|
elif sexp[0]["token"] == 'eval':
|
||||||
|
if len(sexp) != 2:
|
||||||
|
raise Exception("Ln %d, Col %d: the argument number of eval should be 1" %
|
||||||
|
(sexp[0]["line"], sexp[0]["col"]))
|
||||||
|
else:
|
||||||
|
result = self.interprete_aux(sexp[1])
|
||||||
|
return result
|
||||||
|
|
||||||
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" %
|
||||||
|
@ -268,6 +312,8 @@ class Interpreter:
|
||||||
if not is_found:
|
if not is_found:
|
||||||
raise Exception("Ln %d, Col %d: the variable %s is not found!" %
|
raise Exception("Ln %d, Col %d: the variable %s is not found!" %
|
||||||
(sexp[1]["line"], sexp[1]["col"], sexp[1]["token"]))
|
(sexp[1]["line"], sexp[1]["col"], sexp[1]["token"]))
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
elif sexp[0]["token"] == "def-syntax":
|
elif sexp[0]["token"] == "def-syntax":
|
||||||
|
@ -289,6 +335,10 @@ class Interpreter:
|
||||||
|
|
||||||
self.macro_list[syntax_name] = result_list
|
self.macro_list[syntax_name] = result_list
|
||||||
|
|
||||||
|
# add to auto completion list
|
||||||
|
#if self.editor != None:
|
||||||
|
# self.editor.append_autocompletion_item(defined_var)
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
elif sexp[0]["token"] == "begin":
|
elif sexp[0]["token"] == "begin":
|
||||||
|
@ -351,6 +401,58 @@ class Interpreter:
|
||||||
else:
|
else:
|
||||||
raise Exception("Line %d, Col. %d, the form of call is mal-formed." % (sexp[0]["line"], sexp[0]["col"]))
|
raise Exception("Line %d, Col. %d, the form of call is mal-formed." % (sexp[0]["line"], sexp[0]["col"]))
|
||||||
|
|
||||||
|
# make a List class object
|
||||||
|
elif sexp[0]["token"] == "ls":
|
||||||
|
result = []
|
||||||
|
|
||||||
|
if len(sexp) == 1:
|
||||||
|
return result
|
||||||
|
else:
|
||||||
|
result = [self.interprete_aux(x) for x in sexp[1:]]
|
||||||
|
|
||||||
|
return List(result)
|
||||||
|
|
||||||
|
# (car List)
|
||||||
|
elif sexp[0]["token"] == "car":
|
||||||
|
if len(sexp) != 2:
|
||||||
|
raise Exception("Line %d, Col. %d, the argument length should be 1" % (sexp[0]["line"], sexp[0]["col"]))
|
||||||
|
elif not isinstance(sexp[1], List):
|
||||||
|
raise Exception("Line %d, Col. %d, the argument is not a list." % (sexp[1]["line"], sexp[1]["col"]))
|
||||||
|
else:
|
||||||
|
ls = sexp[1].ls
|
||||||
|
return ls[0]
|
||||||
|
|
||||||
|
# (cdr List)
|
||||||
|
elif sexp[0]["token"] == "cdr":
|
||||||
|
if len(sexp) != 2:
|
||||||
|
raise Exception("Line %d, Col. %d, the argument length should be 1" % (sexp[0]["line"], sexp[0]["col"]))
|
||||||
|
elif not isinstance(sexp[1], List):
|
||||||
|
raise Exception("Line %d, Col. %d, the argument is not a list." % (sexp[1]["line"], sexp[1]["col"]))
|
||||||
|
else:
|
||||||
|
ls = sexp[1].ls
|
||||||
|
return ls[1:]
|
||||||
|
|
||||||
|
# (cons any List)
|
||||||
|
elif sexp[0]["token"] == "cons":
|
||||||
|
if len(sexp) != 3:
|
||||||
|
raise Exception("Line %d, Col. %d, the argument length should be 2" % (sexp[0]["line"], sexp[0]["col"]))
|
||||||
|
elif not isinstance(sexp[2], List):
|
||||||
|
raise Exception("Line %d, Col. %d, the 2nd argument of cons is not a list." % (sexp[2]["line"], sexp[2]["col"]))
|
||||||
|
else:
|
||||||
|
car = sexp[1]
|
||||||
|
cdr = sexp[2].ls
|
||||||
|
result_ls = List([sexp[1]]+cdr)
|
||||||
|
return result_ls
|
||||||
|
|
||||||
|
|
||||||
|
elif sexp[0]["token"] == "ls-ref":
|
||||||
|
if len(sexp) != 3:
|
||||||
|
raise Exception("Line %d, Col. %d, the argument length should be 1" % (sexp[0]["line"], sexp[0]["col"]))
|
||||||
|
elif not isinstance(sexp[1], List):
|
||||||
|
raise Exception("Line %d, Col. %d, the 2nd argument of cons is not a list." % (sexp[2]["line"], sexp[2]["col"]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# if it's a sub-xml-element, show the string form of it, or return the input unchanged.
|
# 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'
|
# It's recommended to use it only print it in terminal with 'print'
|
||||||
elif sexp[0]["token"] == "xml-to-string":
|
elif sexp[0]["token"] == "xml-to-string":
|
||||||
|
@ -384,29 +486,34 @@ class Interpreter:
|
||||||
return ET.tostring(self.silexml, encoding="unicode")
|
return ET.tostring(self.silexml, encoding="unicode")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
sexp_new = [self.interprete_aux(x) for x in sexp]
|
ret = self.apply(sexp)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def apply(self, sexp):
|
||||||
|
|
||||||
if isinstance(sexp_new[0], Lambda):
|
sexp_new = [self.interprete_aux(x) for x in sexp]
|
||||||
vars = sexp_new[0].vars
|
|
||||||
env = sexp_new[0].env
|
if isinstance(sexp_new[0], Lambda):
|
||||||
body = sexp_new[0].body
|
vars = sexp_new[0].vars
|
||||||
vars_input = sexp_new[1:]
|
env = sexp_new[0].env
|
||||||
|
body = sexp_new[0].body
|
||||||
|
vars_input = sexp_new[1:]
|
||||||
|
|
||||||
if len(vars) != len(vars_input):
|
if len(vars) != len(vars_input):
|
||||||
raise Exception("Ln %d, Col %d: argument length is not matched." %
|
raise Exception("Ln %d, Col %d: argument length is not matched." %
|
||||||
(self.get_first_item(sexp[0])["line"], self.get_first_item(sexp[0])["col"]))
|
(self.get_first_item(sexp[0])["line"], self.get_first_item(sexp[0])["col"]))
|
||||||
|
|
||||||
new_env_block = dict()
|
new_env_block = dict()
|
||||||
for i in range(len(vars)):
|
for i in range(len(vars)):
|
||||||
new_env_block[vars[i]] = vars_input[i]
|
new_env_block[vars[i]] = vars_input[i]
|
||||||
|
|
||||||
new_env = [new_env_block] + env
|
new_env = [new_env_block] + env
|
||||||
|
|
||||||
old_env = self.env
|
old_env = self.env
|
||||||
self.env = new_env
|
self.env = new_env
|
||||||
ret = self.interprete_aux(body)
|
ret = self.interprete_aux(body)
|
||||||
self.env = old_env
|
self.env = old_env
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def remove_dict_form(self, sexp):
|
def remove_dict_form(self, sexp):
|
||||||
|
@ -426,7 +533,7 @@ class Interpreter:
|
||||||
if isinstance(before_stx[i], list):
|
if isinstance(before_stx[i], list):
|
||||||
unification = self.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"]):
|
||||||
if i == len(before_stx) - 1:
|
if i == len(before_stx) - 1:
|
||||||
unification[before_stx[i]["token"]] = {"content": sexp[i:], "dotted":True}
|
unification[before_stx[i]["token"]] = {"content": sexp[i:], "dotted":True}
|
||||||
|
@ -467,8 +574,14 @@ class SubXMLElement:
|
||||||
def __init__(self, element):
|
def __init__(self, element):
|
||||||
self.element = element
|
self.element = element
|
||||||
|
|
||||||
def __str__(init):
|
def __str__(self):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
# closure
|
||||||
|
class List:
|
||||||
|
def __init__(self, ls):
|
||||||
|
self.ls = ls
|
||||||
|
|
||||||
# closure
|
# closure
|
||||||
class Lambda:
|
class Lambda:
|
||||||
def __init__(self, vars, body, env):
|
def __init__(self, vars, body, env):
|
|
@ -6,7 +6,7 @@ class Parser():
|
||||||
bool_pattern = r"(?P<bool>True|False)"
|
bool_pattern = r"(?P<bool>True|False)"
|
||||||
int_pattern =r"(?P<int>[+-]?\d+)"
|
int_pattern =r"(?P<int>[+-]?\d+)"
|
||||||
symbol_pattern = r"(?P<sym>[_a-zA-Z][-!:._0-9a-zA-Z]*)"
|
symbol_pattern = r"(?P<sym>[_a-zA-Z][-!:._0-9a-zA-Z]*)"
|
||||||
string_pattern = r"(?P<str>[\"]([^\"\\]|[\\][\\\"\n\t]|[\\])*?[\"])"
|
string_pattern = r"(?P<str>[\"]([^\"\\]|[\\][\\\"nt]|[\\])*?[\"])"
|
||||||
parenthesis_pattern = r"(?P<paren>[[]|[]])"
|
parenthesis_pattern = r"(?P<paren>[[]|[]])"
|
||||||
percent_pattern = r"(?P<percent>[%])"
|
percent_pattern = r"(?P<percent>[%])"
|
||||||
space_pattern = r"(?P<space>[ \t]+)"
|
space_pattern = r"(?P<space>[ \t]+)"
|
|
@ -14,13 +14,17 @@ from PyQt5 import QtWebEngineWidgets
|
||||||
from PyQt5.QtWidgets import *
|
from PyQt5.QtWidgets import *
|
||||||
from PyQt5.Qsci import QsciScintilla
|
from PyQt5.Qsci import QsciScintilla
|
||||||
|
|
||||||
from Editor import qrc_resources
|
src_dirname = os.path.dirname(os.path.abspath(os.path.dirname(__file__)))
|
||||||
|
sys.path.append(src_dirname)
|
||||||
|
|
||||||
from Editor import __about__
|
|
||||||
from Editor import FindReplace
|
from Clochur import qrc_resources
|
||||||
from Editor.Interpreter import Interpreter, Lambda
|
|
||||||
from Editorimport CustomQsciEditor
|
from Clochur import __about__
|
||||||
from Editor.Parser import Parser
|
from Clochur import FindReplace
|
||||||
|
from Clochur.Interpreter import Interpreter, Lambda
|
||||||
|
from Clochur import CustomQsciEditor
|
||||||
|
from Clochur.Parser import Parser
|
||||||
|
|
||||||
|
|
||||||
sile_command = 'sile'
|
sile_command = 'sile'
|
||||||
|
@ -175,7 +179,6 @@ class Window(QMainWindow):
|
||||||
format_menu = menuBar.addMenu("&Format")
|
format_menu = menuBar.addMenu("&Format")
|
||||||
format_menu.addAction(self.bold_action)
|
format_menu.addAction(self.bold_action)
|
||||||
format_menu.addAction(self.italic_action)
|
format_menu.addAction(self.italic_action)
|
||||||
format_menu.addAction(self.strike_action)
|
|
||||||
format_menu.addAction(self.underline_action)
|
format_menu.addAction(self.underline_action)
|
||||||
|
|
||||||
help_menu = menuBar.addMenu("&Help")
|
help_menu = menuBar.addMenu("&Help")
|
||||||
|
@ -195,7 +198,11 @@ class Window(QMainWindow):
|
||||||
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], 'r', encoding='utf-8')
|
self.file = open(file_path[0], 'r', encoding='utf-8')
|
||||||
self.editor.setText(self.file.read())
|
self.editor.setText(self.file.read())
|
||||||
|
self.setWindowTitle("Clochur - %s" % self.filename)
|
||||||
|
self.editor.setModified(False)
|
||||||
self.file.close()
|
self.file.close()
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -207,7 +214,7 @@ class Window(QMainWindow):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.file = open(os.path.join(self.opened_file_dirname,self.filename), 'w', encoding='utf-8')
|
self.file = open(os.path.join(self.opened_file_dirname,self.filename), '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 +225,7 @@ class Window(QMainWindow):
|
||||||
with open(os.path.join(self.tmp_folder, self.tmp_file), 'r') as f:
|
with open(os.path.join(self.tmp_folder, self.tmp_file), 'r') as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
data["untitled"].remove(self.untitled_id)
|
data["untitled"].remove(self.untitled_id)
|
||||||
|
self.untitled_id = None
|
||||||
|
|
||||||
with open(os.path.join(self.tmp_folder, self.tmp_file), 'w') as f:
|
with open(os.path.join(self.tmp_folder, self.tmp_file), 'w') as f:
|
||||||
json.dump(data, f, indent=4)
|
json.dump(data, f, indent=4)
|
||||||
|
@ -286,13 +294,17 @@ class Window(QMainWindow):
|
||||||
if self.editor.isModified():
|
if self.editor.isModified():
|
||||||
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)
|
||||||
if reply == QMessageBox.Yes:
|
if reply == QMessageBox.Yes:
|
||||||
file_path = QFileDialog.getSaveFileName(self, 'Save file as...', self.opened_file_dirname, "CLC typesetting format (*.clc)")
|
if self.filename != None:
|
||||||
if file_path[0] != '':
|
self.save_call()
|
||||||
self.file = open(file_path[0], 'w', encoding='utf-8')
|
else:
|
||||||
file_content = editor.text()
|
|
||||||
self.file.write(file_content)
|
file_path = QFileDialog.getSaveFileName(self, 'Save file as...', self.opened_file_dirname, "CLC typesetting format (*.clc)")
|
||||||
self.file.close()
|
if file_path[0] != '':
|
||||||
self.removing_untitled_id()
|
self.file = open(file_path[0], 'w', encoding='utf-8')
|
||||||
|
file_content = self.editor.text()
|
||||||
|
self.file.write(file_content)
|
||||||
|
self.file.close()
|
||||||
|
self.removing_untitled_id()
|
||||||
|
|
||||||
elif reply == QMessageBox.No:
|
elif reply == QMessageBox.No:
|
||||||
self.removing_untitled_id()
|
self.removing_untitled_id()
|
||||||
|
@ -378,7 +390,6 @@ class Window(QMainWindow):
|
||||||
|
|
||||||
formatToolBar.addAction(self.bold_action)
|
formatToolBar.addAction(self.bold_action)
|
||||||
formatToolBar.addAction(self.italic_action)
|
formatToolBar.addAction(self.italic_action)
|
||||||
formatToolBar.addAction(self.strike_action)
|
|
||||||
formatToolBar.addAction(self.underline_action)
|
formatToolBar.addAction(self.underline_action)
|
||||||
|
|
||||||
'''create font adder'''
|
'''create font adder'''
|
BIN
src/example.pdf
BIN
src/example.pdf
Binary file not shown.
Loading…
Reference in a new issue