Skip to content

Commit

Permalink
x2.3r1
Browse files Browse the repository at this point in the history
  • Loading branch information
iiPythonx committed Apr 23, 2022
1 parent 9fb0ad4 commit 24b1c58
Show file tree
Hide file tree
Showing 40 changed files with 1,012 additions and 1,923 deletions.
3 changes: 3 additions & 0 deletions .x2config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"main": "main.x2"
}
3 changes: 0 additions & 3 deletions .xtconfig

This file was deleted.

309 changes: 22 additions & 287 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,301 +1,36 @@
# Copyright 2022 iiPython
# x2.3b1 Codename Goos

# Modules
import os
import sys
import time
import json
import string
from typing import Any, Tuple

from x2 import (
opmap,
XTMemory, XTDatastore, XTContext,
UnknownOperator, InvalidSection, IllegalSectionName
load_sections, load_cli, config,
Interpreter
)

# Initialization
__version__ = "x2.3b1"

sys.argv = sys.argv[1:]
xt_folder = os.path.join(os.path.dirname(__file__), "x2")

def check_argv(matches: list) -> bool:
return bool([m for m in matches if m in sys.argv])

if check_argv(["-h", "--help"]):
print("usage: x2 [-hv] [--help/version] [file]")
print("-h (--help) shows this message and exits")
print("-v (--version) prints the x2 version and exits")
print()
print("If file is not provided, it is loaded from .xtconfig")
print("Copyright (c) 2022 iiPython + Dm123321_31mD")
sys.exit(0)

elif check_argv(["-v", "--version"]):
sys.exit(__version__)

# Load x2 configuration
config = {}
if os.path.isfile(".xtconfig"):
with open(".xtconfig", "r") as f:
config = json.loads(f.read())

# x2 Interpreter
class XTInterpreter(object):
def __init__(self, opmap: dict = {}) -> None:

# Memory initialization
self.memory = XTMemory()
self.memory.interpreter = self

self.linetrk = []
self.sections = {}

self._live = False
self._opmap = opmap

# Data attributes
self._config = config
self._uptime = time.time()
self._version = __version__

def setvar(self, name: str, value: Any, **kwargs) -> Any:
return XTDatastore(self.memory, name, **kwargs).set(value)

def getvar(self, name: str, **kwargs) -> XTDatastore:
return XTDatastore(self.memory, name, **kwargs)

def execute(self, line: str, raise_error: bool = False) -> Any:
if self._live and ((self.linetrk and self.linetrk[-1][0] != "<stdin>") or not self.linetrk):
self.linetrk.append({"file": "<stdin>", "path": "<stdin>", "section": "<stdin>.global", "start": 0, "ended": False, "as": "<stdin>"})

try:
tokens = self.parseline(line)
try:
trkdata = self.linetrk[-1]
prevline = self.sections[trkdata["section"]]["lines"][trkdata["start"] - self.sections[trkdata["section"]]["start"] - 2]
if prevline[-2:] in [" \\", ".."]:
return None

except IndexError:
pass

operator = tokens[0]
if operator not in self._opmap:
raise UnknownOperator(operator)

return self._opmap[operator](XTContext(self.memory, tokens[1:]))

except Exception as e:
if raise_error or True:
raise e

elif config.get("quiet", False):
return None

print("Exception occured in x2 thread!")
for tracker in self.linetrk:
line = self.sections[tracker['section']]["lines"][tracker['start'] - self.sections[tracker['section']]["start"] - 1].lstrip()
print(f"{tracker['file']} line {tracker['start']}, in {tracker['section'].split('.')[1]}:\n > {line}")

print(f"\n{type(e).__name__}: {e}")
if not self._live:
os._exit(1)

def parseline(self, line: str, multiline_offset: int = 0) -> list:
data = {"val": "", "flags": [], "expridx": 0, "line": []}
for idx, char in enumerate(line):
if char == ":" and "qt" not in data["flags"]:
if idx != len(line) - 1 and line[idx + 1] == ":":
break

if char == ")" and "expr" in data["flags"]:
if data["expridx"] > 1:
data["val"] += ")"

elif data["expridx"] == 1:
data["flags"].remove("expr")
data["line"].append(f"({data['val']})")
data["val"] = ""

data["expridx"] -= 1

elif char == "(" and "qt" not in data["flags"]:
if "expr" not in data["flags"]:
data["flags"].append("expr")

data["expridx"] += 1
if data["expridx"] > 1:
data["val"] += "("

elif "expr" in data["flags"]:
data["val"] += char

elif char == " " and "qt" not in data["flags"]:
if not data["val"]:
continue

data["line"].append(data["val"])
data["val"] = ""
cli = load_cli() # Render CLI

elif char == "\"" and (line[idx - 1] != "\\" if idx > 0 else True): # Enables quoting with backslash
if "qt" in data["flags"]:
data["line"].append(data["val"] + "\"")
data["val"] = ""
data["flags"].remove("qt")
# Load filepath
filepath = cli.filepath
if filepath is None:
cli.show_help()

else:
data["flags"].append("qt")
data["val"] += "\""
elif filepath == ".":
filepath = config.get("main", "main.x2")

else:
data["val"] += char
if not os.path.isfile(filepath):
sys.exit("x2 Exception: no such file")

# Construct missing data
if data["val"]:
data["line"].append(data["val"])
data["val"] = ""
# Load file content
with open(filepath, "r") as f:
data = f.read()

# Push lines
if data["line"][-1] in ["\\", ".."]:
trkdata = self.linetrk[-1]
nextline = self.parseline(self.sections[trkdata[1]]["lines"][trkdata[2] - self.sections[trkdata[1]]["start"] + multiline_offset], multiline_offset + 1)
if data["line"][-1] == "..":
data["line"][-2], nextline = data["line"][-2][:-1] + "\n" + nextline[0][1:], nextline[1:]

data["line"] = data["line"][:-1]
data["line"] = data["line"] + nextline

return data["line"]

def load_sections(self, code: str, filepath: str, namespace: str = None, external: bool = False) -> None:
filename = filepath.replace("\\", "/").split("/")[-1]
if not hasattr(self, "_entrypoint"):
self._entrypoint = filename

self.memory.vars["file"][filepath] = {}

fileid = (namespace or filepath.replace("/", ".")).removesuffix(".xt")
dt = {
"active": "global",
"code": [],
"sections": {f"{fileid}.global": {
"lines": [], "priv": False, "file": filename, "path": filepath,
"start": 0, "args": [], "ret": None, "as": fileid
}}
}
for lno, line in enumerate(code.split("\n")):
if line.strip():
if line[0] == ":" and line[:2] != "::":
ns, sid, priv = f"{fileid}.{dt['active']}", line[1:].split(" ")[0], False
if [c for c in sid if c not in string.ascii_letters + string.digits + "@"]:
raise IllegalSectionName(f"section '{sid}' contains invalid characters")

elif "@" in sid:
if sid[0] != "@":
raise IllegalSectionName("section name cannot contain a '@' unless it indicates a private section")

priv, sid = True, sid[1:]

dt["sections"][ns]["lines"] = dt["code"]
dt["sections"][f"{fileid}.{sid}"] = {
"file": filename, "start": lno + 1, "lines": [], "priv": priv,
"args": line.split(" ")[1:], "ret": None, "as": fileid, "path": filepath
}
dt["code"] = []
dt["active"] = sid
continue

dt["code"].append(line.lstrip())

if dt["code"]:
dt["sections"][f"{fileid}.{dt['active']}"]["lines"] = dt["code"]

self.sections = {**self.sections, **dt["sections"]}
if external:
self.run_section(f"{fileid}.global")
del self.sections[f"{fileid}.global"] # Save memory

def find_section(self, section: str) -> Tuple[str, str]:
current_file = (self.linetrk[-1]["path"] if self.linetrk else self._entrypoint).removesuffix(".xt").replace("/", ".")
if "." not in section:
section = f"{current_file}.{section}"

if section not in self.sections:
raise InvalidSection(section)

return section, current_file

def run_section(self, section: str) -> Any:
section, current_file = self.find_section(section)
secdata = section.split(".")
s = self.sections[section]
if s["priv"] and current_file + ".xt" != s["file"]:
raise InvalidSection(f"{secdata[1]} is a private section and cannot be called")

if section not in self.memory.vars["local"]:
self.memory.vars["local"][section] = {}

self.linetrk.append({"file": s["file"], "path": s["path"], "section": section, "start": s["start"], "ended": False, "as": s["as"]})
for line in s["lines"]:
self.linetrk[-1]["start"] += 1
if line.strip() and line[:2] != "::":
self.execute(line)
if self.linetrk[-1]['ended']:
break

del self.memory.vars["local"][section]

self.linetrk.pop()
return s["ret"]

# Handler
inter = XTInterpreter(opmap)
if not sys.argv:
print(f"{__version__} Copyright (c) 2022 iiPython")
inter._live, linedata = True, ""
inter.load_sections(":global\n", "<stdin>")
inter.memory.vars["local"]["<stdin>.global"] = {}
while True:
try:
line = input(f"{'>' if not linedata else ':'} ")
if line[:2] == "::":
continue

elif linedata and not line.strip():
inter.load_sections(linedata, "<stdin>")
linedata = ""

elif not line.strip():
continue

elif line[0] == ":" and line[:2] != "::":
linedata = line + "\n"

elif linedata:
linedata += line + "\n"

else:
inter.execute(line)

except KeyboardInterrupt:
os._exit(0)

else:
file = sys.argv[0]
if file == ".":
file = config.get("main", "main.xt")

try:
with open(file, "r", encoding = "utf-8") as f:
code = f.read()

except Exception:
print("x2: failed to load file")
os._exit(1)

inter.load_sections(code, file)
file = file.replace("\\", "/").replace("/", ".").removesuffix(".xt").removeprefix("./")
[inter.run_section(s) for s in [f"{file}.global", f"{file}.main"]]
# Run file
sections = load_sections(data, filepath)
interpreter = Interpreter(
filepath, sections,
config = config,
cli_vals = cli.vals
)
interpreter.run_section("main")
10 changes: 10 additions & 0 deletions main.x2
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
:: Simple x2 program

:: This will run just fine
prt "Hello, world!"

:: This will output color if you use -c
prt "[green]Hello, world![/]"

:: Nice note
prt "\nNo color? Run the interpreter with the -c flag."
2 changes: 0 additions & 2 deletions main.xt

This file was deleted.

22 changes: 0 additions & 22 deletions pkg/iipython/main.xt

This file was deleted.

Loading

0 comments on commit 24b1c58

Please sign in to comment.