Skip to content

Commit

Permalink
fix(parser):fix error 36 in a certain circumstance
Browse files Browse the repository at this point in the history
Fix error 36 when receiving eof after a literal.
Fix exception inhertance.
Now KoiLangCommandError will be raised
when the command not found in the command set.
  • Loading branch information
Ovizro committed Aug 27, 2022
1 parent efa8d5b commit 3e0403f
Show file tree
Hide file tree
Showing 11 changed files with 1,888 additions and 936 deletions.
67 changes: 64 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ All the arguments can be put together

## What can Kola module do

Kola module provides a fast way to convert KoiLang command
to a python function call.
Kola module provides a fast way to translate KoiLang command
into a python function call.

Above command `#draw` will convert to function calling below:
Above command `#draw` will convert to function call below:

```py
draw(
Expand All @@ -74,3 +74,64 @@ draw(
)
```

Well, Kola do not know the exact implementation of a command, so a command set is needed to provide. Just create a subclass of `KoiLang` and use decorator `@kola_command` on the command functions. Here is a fast example script.

```py
import os
from kola import KoiLang, kola_command, kola_text


class MultiFileManager(KoiLang):
def __init__(self) -> None:
self._file = None
super().__init__()

def __del__(self) -> None:
if self._file:
self._file.close()

@kola_command
def file(self, path: str, encoding: str = "utf-8") -> None:
if self._file:
self._file.close()
path_dir = os.path.dirname(path)
if path_dir:
os.makedirs(path_dir, exist_ok=True)
self._file = open(path, "w", encoding=encoding)

@kola_command
def close(self) -> None:
if self._file:
self._file.close()
self._file = None

@kola_text
def text(self, text: str) -> None:
if not self._file:
raise OSError("write texts before the file open")
self._file.write(text)
```

Then make a simple kola file.

```
#file "hello.txt"
Hello world!
#file "test.txt"
This is a text.
#close
```

And input this in terminal:
```
python -m kola kolafile.kola -s script.py
```

Or directly add in script:
```py
if __name__ = "__main__":
FMultiFileManager().parse_file("kolafile.kola")
```

You will see new files in your work dir.
6 changes: 6 additions & 0 deletions examples/example1.kola
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#file "hello.txt"
Hello world!

#file "test.txt"
This is a text.
#close
33 changes: 33 additions & 0 deletions examples/example1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import os
from kola import KoiLang, kola_command, kola_text


class MultiFileManager(KoiLang):
def __init__(self) -> None:
self._file = None
super().__init__()

def __del__(self) -> None:
if self._file:
self._file.close()

@kola_command
def file(self, path: str, encoding: str = "utf-8") -> None:
if self._file:
self._file.close()
path_dir = os.path.dirname(path)
if path_dir:
os.makedirs(path_dir, exist_ok=True)
self._file = open(path, "w", encoding=encoding)

@kola_command
def close(self) -> None:
if self._file:
self._file.close()
self._file = None

@kola_text
def text(self, text: str) -> None:
if not self._file:
raise OSError("write texts before the file open")
self._file.write(text)
7 changes: 7 additions & 0 deletions kola/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@
from .version import __version__, version_info
from .exception import *

__author__ = "Ovizro"
__author_email__ = "[email protected]"

__all__ = [
"KoiLang",
"kola_command",
"kola_text",
"kola_number",

"BaseLexer",
"FileLexer",
"StringLexer",
Expand Down
165 changes: 141 additions & 24 deletions kola/__main__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,43 @@
"""
Copyright 2022 Ovizro
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import sys
from argparse import ArgumentParser
from traceback import print_exc
from typing import Any, Callable, Dict, Tuple

from kola.klvm import KoiLang, KoiLangMeta, kola_command, kola_text
from .lexer import BaseLexer, FileLexer, StringLexer
from .parser import Parser
from .klvm import KoiLang, KoiLangMeta, kola_command, kola_number, kola_text
from .exception import KoiLangCommandError, KoiLangError

from . import BaseLexer, FileLexer, StringLexer, Parser, __version__
from . import __version__


def _load_script(path: str, encoding: str = "utf-8") -> KoiLangMeta:
vdict = {}
with open(path, encoding=encoding) as f:
exec(
compile(f.read(), path, "exec"),
vdict
)
for i in vdict.values():
if isinstance(i, KoiLangMeta) and i is not KoiLang:
return i
else:
raise TypeError("no KoiLang command set found")


class CommandDebugger:
Expand All @@ -15,19 +48,66 @@ def wrapper(*args, **kwds) -> None:


class KoiLangMain(KoiLang):
def __init__(self) -> None:
self.vars = {}
super().__init__()

@kola_command
def version(self):
def version(self) -> None:
print(__version__)

@kola_text
def text(self, text: str):
@kola_command
def author(self, author: str = "name") -> None:
if author == "name":
print("Ovizro")
elif author == "email":
print("[email protected]")
else:
raise KoiLangCommandError("author information only supports 'name' and 'email'")

@kola_command
def license(self) -> None:
print(__doc__)

@kola_command
def raises(self) -> None:
raise KoiLangCommandError

@kola_command
def echo(self, text: str) -> None:
print(text)

@kola_command
def get(self, key: str) -> None:
print(self.vars.get(key, None))

@kola_command
def set(self, **kwds) -> None:
self.vars.update(kwds)

@kola_command
def load(self, path: str, type: str = "kola", *, encoding: str = "utf-8") -> None:
if type == "kola":
self.parse_file(path)
elif type == "script":
cmd_set = _load_script(path, encoding=encoding)()
self.command_set.update(cmd_set.command_set)
else:
raise KoiLangCommandError("load type only supports 'kola' and 'script'")

@kola_text
def text(self, text: str) -> None:
print(f"::{text}")

@kola_command
def exit(self, code: int = 0) -> None:
sys.exit(code)


parser = ArgumentParser("kola")
parser.add_argument("file", default=None, nargs="?")
parser.add_argument("-i", "--inline", help="parse inline string")
parser.add_argument("-p", "--parser", help="parser script")
parser.add_argument("-s", "--script", help="parser script")
parser.add_argument("-d", "--debug", help="dubugger type", choices=["token", "grammar"])

namespace = parser.parse_args()
Expand All @@ -38,28 +118,65 @@ def text(self, text: str):
elif namespace.inline:
lexer = StringLexer(namespace.inline)
else:
lexer = BaseLexer()
lexer = None

if namespace.debug == "token":
print(f"KoiLang Token Debugger {__version__} in file \"{lexer.filename}\" on {sys.platform}")
for i in lexer:
print(i)
if lexer is None:
print(f"KoiLang Token Debugger {__version__} on {sys.platform}")
lexer = BaseLexer()
while True:
try:
for i in lexer:
print(i)
break
except KeyboardInterrupt:
break
except KoiLangError:
print_exc(5)

elif namespace.debug == "grammar":
print(f"KoiLang Grammar Debugger {__version__} in file \"{lexer.filename}\" on {sys.platform}")
Parser(lexer, CommandDebugger).exec_()
else:
print(f"KoiLang Runner in file \"{lexer.filename}\" on {sys.platform}")
if namespace.parser:
vdict = {}
with open(namespace.parser) as f:
exec(f.read(), {}, vdict)
for i in vdict.values():
if isinstance(i, KoiLangMeta):
command_cls = i
if lexer:
Parser(lexer, CommandDebugger).exec_()
else:
print(f"KoiLang Grammar Debugger {__version__} on {sys.platform}")
while True:
try:
sys.stdout.write("$kola: ")
sys.stdout.flush()
i = sys.stdin.readline()
while i.endswith("\\\n"):
sys.stdout.write("... :")
sys.stdout.flush()
i += sys.stdin.readline()
Parser(StringLexer(i), CommandDebugger).exec_once()
except KeyboardInterrupt:
break
else:
raise TypeError("no KoiLang command set found")
except KoiLangError:
print_exc(5)

else:
if namespace.script:
command_cls = _load_script(namespace.script)
else:
command_cls = KoiLangMain

Parser(lexer, command_cls().command_set).exec_()
command_set = command_cls()
if lexer:
command_set.parse(lexer)
else:
print(f"KoiLang Runner {__version__} on {sys.platform}")
while True:
try:
sys.stdout.write("$kola: ")
sys.stdout.flush()
i = sys.stdin.readline()
while i.endswith("\\\n"):
sys.stdout.write("... :")
sys.stdout.flush()
i += sys.stdin.readline()
command_set.parse(i)
except KeyboardInterrupt:
break
except KoiLangError:
print_exc(5)

10 changes: 3 additions & 7 deletions kola/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@ class KoiLangError(Exception):
__slots__ = []


class KoiLangSyntaxError(Exception):
class KoiLangSyntaxError(KoiLangError):
"""
Raised when failing to parse text
"""
__slots__ = ["err_code"]

def __init__(self, *args: object) -> None:
super().__init__(*args)
self.err_code = 0
__slots__ = []


class KoiLangCommandError(Exception):
class KoiLangCommandError(KoiLangError):
"""
Raised when errors occure during executing commands
"""
Expand Down
8 changes: 3 additions & 5 deletions kola/klvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,9 @@ def __init__(self) -> None:
def __getitem__(self, key: str) -> Callable:
return self.command_set[key]

def parse(self, text: Optional[str] = None) -> None:
if text is None:
lexer = BaseLexer()
else:
lexer = StringLexer(text)
def parse(self, lexer: Union[BaseLexer, str]) -> None:
if isinstance(lexer, str):
lexer = StringLexer(lexer)
Parser(lexer, self.command_set).exec_()

def parse_file(self, path: str) -> None:
Expand Down
Loading

0 comments on commit 3e0403f

Please sign in to comment.