mirror of
https://github.com/RGBCube/CSAssignments
synced 2025-06-20 04:52:10 +00:00
Oops
This commit is contained in:
parent
df3ebcd560
commit
57f4be66d2
20 changed files with 322 additions and 1681 deletions
1241
.editorgconfig
1241
.editorgconfig
File diff suppressed because it is too large
Load diff
9
.fleet/run.json
Normal file
9
.fleet/run.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"type": "python",
|
||||
"name": "Run Interactive Runner",
|
||||
"arguments": ["-m", "interactive_runner"],
|
||||
}
|
||||
]
|
||||
}
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -39,3 +39,5 @@ run/
|
|||
*.ipr
|
||||
*.iws
|
||||
compiled/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
|
|
@ -11,15 +11,15 @@ git clone https://github.com/RGBCube/CSAssignments
|
|||
cd CSAssignments
|
||||
```
|
||||
|
||||
OK, now you have the code on your machine. You can use the interactive code runner script to run the
|
||||
code
|
||||
Now you have the code on your machine. You can use the interactive code runner script to run the
|
||||
code.
|
||||
|
||||
## Running
|
||||
|
||||
## Linux
|
||||
|
||||
```bash
|
||||
./run
|
||||
./run.sh
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
|
3
interactive_runner/__init__.py
Normal file
3
interactive_runner/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from .assignment import *
|
||||
from .language import *
|
||||
from .sources import *
|
24
interactive_runner/__main__.py
Normal file
24
interactive_runner/__main__.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
import interactive_runner as lib
|
||||
|
||||
s = lib.Sources()
|
||||
for l in s.languages.values():
|
||||
print("lname",l.name)
|
||||
print("lsname",l.styled_name)
|
||||
print("ldesc",l.description)
|
||||
print("lbc",l._build_command)
|
||||
print("ldni",l.check_dependencies_installed())
|
||||
print("lisc",l.is_compiled)
|
||||
print("lrcmd",l._run_command)
|
||||
print()
|
||||
for a in l.assignments.values():
|
||||
print("alang",a.language)
|
||||
print("aname",a.name)
|
||||
print("agivend",a.given_date)
|
||||
print("adesc",a.description)
|
||||
print("adirs",a.directions)
|
||||
try:
|
||||
a.run()
|
||||
except Exception as e:
|
||||
print("errrun",e)
|
||||
print()
|
||||
|
85
interactive_runner/assignment.py
Normal file
85
interactive_runner/assignment.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
from __future__ import annotations
|
||||
|
||||
__all__ = ("Assignment",)
|
||||
|
||||
from functools import cached_property
|
||||
from typing import TypedDict, TYPE_CHECKING
|
||||
from tomllib import loads as decode_toml
|
||||
from os import system as cmd
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .language import Language
|
||||
|
||||
AssignmentConfig = TypedDict(
|
||||
"AssignmentConfig",
|
||||
{
|
||||
"name": str,
|
||||
"given-date": str,
|
||||
"main-file": str,
|
||||
"description": str,
|
||||
"directions": str,
|
||||
}
|
||||
)
|
||||
|
||||
class Assignment:
|
||||
__config: AssignmentConfig
|
||||
language: Language
|
||||
name: str
|
||||
given_date: str
|
||||
__main_file: str
|
||||
description: str
|
||||
directions: str
|
||||
|
||||
def __init__(self, directory: Path, language: Language) -> None:
|
||||
self.__directory = directory
|
||||
self.language = language
|
||||
|
||||
@cached_property
|
||||
def __config(self) -> AssignmentConfig:
|
||||
return decode_toml((self.__directory / "assignment.toml").read_text())
|
||||
|
||||
def refresh(self) -> None:
|
||||
del self.__config
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.__config["name"]
|
||||
|
||||
@property
|
||||
def given_date(self) -> str:
|
||||
return self.__config["given-date"]
|
||||
|
||||
@property
|
||||
def __main_file(self) -> Path:
|
||||
return self.__directory / self.__config["main-file"]
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return self.__config["description"]
|
||||
|
||||
@property
|
||||
def directions(self) -> str:
|
||||
return self.__config["directions"]
|
||||
|
||||
def compile(self, *, quiet: bool) -> int | None:
|
||||
if missing := self.language.check_dependencies_installed():
|
||||
raise ValueError(f"Needed depencencies are not installed: {', '.join(missing)}")
|
||||
|
||||
if not self.language.is_compiled:
|
||||
return None
|
||||
|
||||
return cmd(self.language._build_command.format(
|
||||
**{
|
||||
"out-file": self.__directory / "compiled.out",
|
||||
"main-file": self.__main_file.absolute(),
|
||||
}
|
||||
) +
|
||||
(QUIET_SUFFIX if quiet else "")
|
||||
)
|
||||
|
||||
def run(self) -> int:
|
||||
if self.language.is_compiled and not (self.__directory / "compiled.out").exists():
|
||||
self.compile(quiet=True)
|
||||
|
||||
return cmd(self.language._run_command.format((self.__directory / "compiled.out").absolute() if self.language.is_compiled else self.__main_file.absolute()))
|
||||
|
10
interactive_runner/consts.py
Normal file
10
interactive_runner/consts.py
Normal file
|
@ -0,0 +1,10 @@
|
|||
__all__ = ("ROOT", "IS_WINDOWS", "QUIET_SUFFIX", "CHECK_COMMAND_EXISTS", "OS_KEY")
|
||||
|
||||
from os import name as os_name
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).parent.parent
|
||||
IS_WINDOWS = os_name == "nt"
|
||||
QUIET_SUFFIX = " | Out-Null" if IS_WINDOWS else " > /dev/null"
|
||||
CHECK_COMMAND_EXISTS = ("Get-Command {}" if IS_WINDOWS else "command -v {}") + QUIET_SUFFIX
|
||||
OS_KEY = "windows" if IS_WINDOWS else "unix"
|
135
interactive_runner/language.py
Normal file
135
interactive_runner/language.py
Normal file
|
@ -0,0 +1,135 @@
|
|||
from __future__ import annotations
|
||||
|
||||
__all__ = ("Language",)
|
||||
|
||||
from .assignment import Assignment
|
||||
from .consts import OS_KEY, CHECK_COMMAND_EXISTS
|
||||
from typing import TYPE_CHECKING, TypedDict
|
||||
from tomllib import loads as decode_toml
|
||||
from chalky import hex
|
||||
from functools import cached_property
|
||||
from os import system as cmd
|
||||
from chalky.shortcuts.sty import bold
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
LanguageConfig = TypedDict(
|
||||
"LanguageConfig",
|
||||
{
|
||||
"name": str,
|
||||
"descripton": str,
|
||||
"colors": TypedDict(
|
||||
"ColorConfig",
|
||||
{
|
||||
"foregroud": int,
|
||||
"background": int,
|
||||
}
|
||||
),
|
||||
"build": None | TypedDict(
|
||||
"BuildConfig",
|
||||
{
|
||||
"common": str | None,
|
||||
"unix": str | None,
|
||||
"windows": str | None,
|
||||
}
|
||||
),
|
||||
"run": TypedDict(
|
||||
"RunConfig",
|
||||
{
|
||||
"common": str | None,
|
||||
"unix": str | None,
|
||||
"windows": str | None,
|
||||
}
|
||||
),
|
||||
"dependencies": None | TypedDict(
|
||||
"DependencyConfig",
|
||||
{
|
||||
"common": list[str] | None,
|
||||
"unix": list[str] | None,
|
||||
"windows": list[str] | None,
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
class Language:
|
||||
__directory: Path
|
||||
__config: LanguageConfig
|
||||
name: str
|
||||
styled_name: str
|
||||
description: str
|
||||
__dependencies: list[str] | None
|
||||
_build_command: str | None
|
||||
is_compiled: bool
|
||||
_run_command: str
|
||||
assignments: dict[str, Assignment]
|
||||
|
||||
def __init__(self, directory: Path) -> None:
|
||||
self.__directory = directory
|
||||
|
||||
@cached_property
|
||||
def __config(self) -> LanguageConfig:
|
||||
return decode_toml((self.__directory / "language.toml").read_text())
|
||||
|
||||
def refresh(self) -> None:
|
||||
del self.__config
|
||||
del self.assignments
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.__config["name"]
|
||||
|
||||
@property
|
||||
def styled_name(self) -> str:
|
||||
colors = self.__config["colors"]
|
||||
# hack
|
||||
return hex(colors["foreground"]) & hex(colors["background"], background=True) | self.name
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return self.__config["description"]
|
||||
|
||||
@property
|
||||
def __dependencies(self) -> list[str] | None:
|
||||
dependencies = self.__config.get("dependencies", {})
|
||||
return dependencies.get("common", dependencies.get(OS_KEY))
|
||||
|
||||
def check_dependencies_installed(self) -> list[str]:
|
||||
if self.__dependencies is None:
|
||||
return []
|
||||
|
||||
not_installed = []
|
||||
for dependency in self.__dependencies:
|
||||
exit = cmd(CHECK_COMMAND_EXISTS.format(dependency))
|
||||
|
||||
if exit:
|
||||
not_installed.append(dependency)
|
||||
|
||||
return not_installed
|
||||
|
||||
@property
|
||||
def _build_command(self) -> str | None:
|
||||
build = self.__config.get("build", {})
|
||||
return build.get("common", build.get(OS_KEY))
|
||||
|
||||
@property
|
||||
def is_compiled(self) -> bool:
|
||||
return bool(self._build_command)
|
||||
|
||||
@property
|
||||
def _run_command(self) -> str:
|
||||
run = self.__config["run"]
|
||||
return run.get("common", run.get(OS_KEY))
|
||||
|
||||
@cached_property
|
||||
def assignments(self) -> dict[str, Assignment]:
|
||||
assignments = {}
|
||||
|
||||
for assignment_directory in self.__directory.iterdir():
|
||||
if not assignment_directory.is_dir():
|
||||
continue
|
||||
|
||||
assignments[assignment_directory.name] = Assignment(assignment_directory, self)
|
||||
|
||||
return assignments
|
33
interactive_runner/sources.py
Normal file
33
interactive_runner/sources.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
from __future__ import annotations
|
||||
|
||||
__all__ = ("Sources",)
|
||||
|
||||
from .language import Language
|
||||
from .consts import ROOT
|
||||
from functools import cached_property
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
class Sources:
|
||||
__directory: Path
|
||||
languages: dict[str, Language]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.__directory = ROOT / "sources"
|
||||
|
||||
@cached_property
|
||||
def languages(self) -> dict[str, Language]:
|
||||
languages = {}
|
||||
|
||||
for language_directory in self.__directory.iterdir():
|
||||
if not language_directory.is_dir():
|
||||
continue
|
||||
|
||||
languages[language_directory.name] = Language(language_directory)
|
||||
|
||||
return languages
|
||||
|
||||
def refresh(self) -> None:
|
||||
del self.languages
|
280
run
280
run
|
@ -1,280 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import cached_property
|
||||
from os import name as os_name, system as cmd
|
||||
from pathlib import Path
|
||||
from typing import TypeVar
|
||||
|
||||
from chalky import hex, sty
|
||||
|
||||
T = TypeVar("T")
|
||||
ROOT = Path(__file__).parent
|
||||
is_windows = os_name == "nt"
|
||||
|
||||
|
||||
class Sources:
|
||||
def __init__(self) -> None:
|
||||
self.__sources_dir = (ROOT / "src")
|
||||
|
||||
@cached_property
|
||||
def languages(self) -> dict[str, Language]:
|
||||
languages = {}
|
||||
|
||||
i = 0
|
||||
for ldr in self.__sources_dir.iterdir():
|
||||
if ldr.is_dir():
|
||||
i += 1
|
||||
language = Language(ldr)
|
||||
languages[language.name] = language
|
||||
languages[str(i)] = language
|
||||
|
||||
return languages
|
||||
|
||||
|
||||
class Language:
|
||||
def __init__(self, path: Path) -> None:
|
||||
self.__path = path
|
||||
|
||||
@cached_property
|
||||
def name(self) -> str:
|
||||
return self.__path.name
|
||||
|
||||
@cached_property
|
||||
def colored_name(self) -> str:
|
||||
return hex((self.__path / "FGCOLOR").read_text()) & \
|
||||
hex((self.__path / "BGCOLOR").read_text(), background=True) \
|
||||
| self.__path.name
|
||||
|
||||
@cached_property
|
||||
def description(self) -> str:
|
||||
return (self.__path / "DESCRIPTION").read_text()
|
||||
|
||||
@cached_property
|
||||
def is_compiled(self) -> bool:
|
||||
return (self.__path / "COMPILECMD").exists()
|
||||
|
||||
@cached_property
|
||||
def compile_command(self) -> str:
|
||||
"""For compiled languages. Format with `main` and `out`"""
|
||||
return (self.__path / "COMPILECMD").read_text().strip() if self.is_compiled else None
|
||||
|
||||
@cached_property
|
||||
def run_command(self) -> str:
|
||||
"""For interpreted languages. Format with `main`"""
|
||||
return (self.__path / "RUNCMD").read_text().strip()
|
||||
|
||||
@cached_property
|
||||
def assignments(self) -> dict[str, Assignment]:
|
||||
assignments = {}
|
||||
|
||||
i = 0
|
||||
for adr in self.__path.iterdir():
|
||||
if adr.is_dir():
|
||||
i += 1
|
||||
assignment = Assignment(adr, self)
|
||||
assignments[assignment.name] = assignment
|
||||
assignments[str(i)] = assignment
|
||||
|
||||
return assignments
|
||||
|
||||
|
||||
class Assignment:
|
||||
def __init__(self, path: Path, language: Language) -> None:
|
||||
self.__path = path
|
||||
self.language = language
|
||||
|
||||
@cached_property
|
||||
def name(self) -> str:
|
||||
return self.__path.name.split(":", 1)[0]
|
||||
|
||||
@cached_property
|
||||
def date(self) -> str:
|
||||
return self.__path.name.split(":", 1)[1]
|
||||
|
||||
@cached_property
|
||||
def description(self) -> str:
|
||||
return (self.__path / "DESCRIPTION").read_text()
|
||||
|
||||
@cached_property
|
||||
def directions(self) -> str:
|
||||
return (self.__path / "ASSIGNMENT").read_text()
|
||||
|
||||
@cached_property
|
||||
def main_file(self) -> Path:
|
||||
return self.__path / (self.__path / "MAIN").read_text()
|
||||
|
||||
@cached_property
|
||||
def compiled_file(self) -> Path:
|
||||
return ROOT / "compiled" / (self.name + (".exe" if is_windows else ""))
|
||||
|
||||
def compile(self, *, show: bool) -> bool:
|
||||
"""Returns True if compilation was successful, False otherwise."""
|
||||
if self.compiled_file.exists():
|
||||
self.compiled_file.unlink()
|
||||
return cmd(
|
||||
self.language.compile_command.format(main=self.main_file, out=self.compiled_file)
|
||||
+ "" if show else (" > NUL" if is_windows else " > /dev/null")
|
||||
) == 0
|
||||
|
||||
def run(self) -> None:
|
||||
if self.language.is_compiled:
|
||||
if not self.compiled_file.exists():
|
||||
self.compile(show=False)
|
||||
cmd(self.compiled_file.absolute())
|
||||
else:
|
||||
cmd(self.language.run_command.format(main=self.main_file.absolute()))
|
||||
|
||||
|
||||
src = Sources()
|
||||
|
||||
red = hex("ff0000")
|
||||
green = hex("39ff14")
|
||||
cyan = hex("4bf0fc")
|
||||
purple = hex("b026ff")
|
||||
orange = hex("ff760d")
|
||||
|
||||
invalid_choice = red | "Invalid choice! Try again."
|
||||
exiting = red | "Exiting..."
|
||||
|
||||
|
||||
def nl() -> None:
|
||||
print()
|
||||
|
||||
|
||||
_nl = nl
|
||||
|
||||
|
||||
def do_exit(s: str, /) -> str:
|
||||
if s == "exit":
|
||||
print(exiting)
|
||||
exit()
|
||||
return s
|
||||
|
||||
|
||||
def nprnt(*s, nl: bool = False) -> None:
|
||||
print(*s)
|
||||
if nl:
|
||||
_nl()
|
||||
|
||||
|
||||
def scan(prompt: str, /, *, nl: bool = False) -> str:
|
||||
s = input(prompt).lower().strip()
|
||||
if nl:
|
||||
_nl()
|
||||
return do_exit(s)
|
||||
|
||||
|
||||
def yesno(prompt: str, /, *, nl: bool = False) -> bool:
|
||||
s = input(prompt).lower().strip()
|
||||
if s in {"y", "yes"}:
|
||||
return True
|
||||
elif s in {"n", "no"}:
|
||||
return False
|
||||
else:
|
||||
print(invalid_choice)
|
||||
return yesno(prompt, nl=nl)
|
||||
|
||||
|
||||
def iter_int_keys(d: dict[str, T]) -> list[tuple[int, T]]:
|
||||
for k, v in d.items():
|
||||
if k.isdigit():
|
||||
yield int(k), v
|
||||
|
||||
|
||||
def get_ignorecase(d: dict[str, T], k: str) -> T:
|
||||
for key, value in d.items():
|
||||
# k is always lowercase
|
||||
if key.lower() == k:
|
||||
return value
|
||||
raise KeyError(k)
|
||||
|
||||
|
||||
while True:
|
||||
# TODO align columns
|
||||
print("----------", green & sty.bold | "LANGUAGES", "----------")
|
||||
|
||||
for i, language in iter_int_keys(src.languages):
|
||||
print((green | i) + f": {language.colored_name} - {language.description}")
|
||||
nl()
|
||||
|
||||
choice = scan("Choose a language by its number or name (exit to exit): ", nl=True)
|
||||
|
||||
language = get_ignorecase(src.languages, choice)
|
||||
|
||||
if language is None:
|
||||
nprnt(invalid_choice, nl=True)
|
||||
continue
|
||||
|
||||
while True:
|
||||
# TODO align columns
|
||||
print("----------", cyan & sty.bold | language.name.upper(), "----------")
|
||||
|
||||
for i, assignment in iter_int_keys(language.assignments):
|
||||
print(
|
||||
(cyan | i) + f": {assignment.name} ({assignment.date}) - {assignment.description}"
|
||||
)
|
||||
nl()
|
||||
|
||||
choice = scan(
|
||||
"Choose an assignment by its number or name (back to go back, exit to exit): ",
|
||||
nl=True
|
||||
)
|
||||
|
||||
if choice == "back":
|
||||
break
|
||||
|
||||
assignment = get_ignorecase(language.assignments, choice)
|
||||
|
||||
if assignment is None:
|
||||
nprnt(invalid_choice, nl=True)
|
||||
continue
|
||||
|
||||
while True:
|
||||
print("----------", purple & sty.bold | assignment.name.upper(), "----------")
|
||||
print((purple | 1) + ": Show directions")
|
||||
if language.is_compiled:
|
||||
print((purple | 2) + ": Compile (uses cache if exists) & run")
|
||||
print((purple | 3) + ": Compile manually")
|
||||
else:
|
||||
print((purple | 2) + ": Run")
|
||||
nl()
|
||||
|
||||
choice = scan(
|
||||
"Choose an option by its number (back to go back, exit to exit): ",
|
||||
nl=True
|
||||
)
|
||||
|
||||
match choice:
|
||||
case "back":
|
||||
break
|
||||
|
||||
case "1":
|
||||
print(
|
||||
"----------",
|
||||
orange & sty.bold | f"DIRECTIONS FOR {assignment.name.upper()}",
|
||||
"----------"
|
||||
)
|
||||
nprnt(assignment.directions, nl=True)
|
||||
|
||||
case "2":
|
||||
nprnt("----------", orange | "RUN OUTPUT", "----------", nl=True)
|
||||
assignment.run()
|
||||
nl()
|
||||
|
||||
case "3":
|
||||
if not language.is_compiled:
|
||||
nprnt(invalid_choice, nl=True)
|
||||
continue
|
||||
|
||||
ask = yesno("Show compile output if exists? (y/n): ", nl=True)
|
||||
print(orange | "Compiling...")
|
||||
success = assignment.compile(show=ask)
|
||||
if success:
|
||||
s = green | "Compiled successfully!"
|
||||
else:
|
||||
s = red | "Compilation failed!"
|
||||
nprnt(s, nl=True)
|
||||
|
||||
case _:
|
||||
nprnt(invalid_choice, nl=True)
|
3
run.bat
3
run.bat
|
@ -1 +1,2 @@
|
|||
py run
|
||||
py -m pip install -U chalky > NUL
|
||||
py -m interactive_runner
|
||||
|
|
137
run.py
137
run.py
|
@ -1,137 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
from __future__ import annotations
|
||||
|
||||
from tomllib import loads as decode_toml
|
||||
from dataclasses import dataclass
|
||||
from functools import cached_property
|
||||
from os import name as os_name, system as cmd
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from chalky import Chalk
|
||||
|
||||
ROOT = Path(__file__).parent
|
||||
IS_WIN = os_name == "nt"
|
||||
QUIET_SUFFIX = " > NUL" if IS_WIN else " > /dev/null"
|
||||
OS_KEY = "windows" if IS_WIN else "linux"
|
||||
|
||||
|
||||
class Sources:
|
||||
def __init__(self) -> None:
|
||||
self.__src_dir = ROOT / "src"
|
||||
|
||||
@cached_property
|
||||
def languages(self) -> dict[str, Language]:
|
||||
languages = {}
|
||||
|
||||
for language_dir in self.__src_dir.iterdir():
|
||||
if not language_dir.is_dir():
|
||||
continue
|
||||
|
||||
language_metadata = decode_toml((language_dir / "language.toml").read_text())
|
||||
language_metadata["directory"] = language_dir
|
||||
|
||||
language = Language.from_raw(language_metadata)
|
||||
|
||||
languages[language.name] = language
|
||||
|
||||
return languages
|
||||
|
||||
|
||||
@dataclass
|
||||
class Colors:
|
||||
fg: Chalk
|
||||
bg: Chalk
|
||||
|
||||
|
||||
class Language:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
directory: Path,
|
||||
|
||||
name: str,
|
||||
description: str,
|
||||
colors: Colors,
|
||||
build_command: str | None,
|
||||
run_command: str,
|
||||
) -> None:
|
||||
self.__directory = directory
|
||||
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.colors = colors
|
||||
self.build_command = build_command
|
||||
self.run_command = run_command
|
||||
|
||||
@classmethod
|
||||
def from_raw(cls, raw: dict) -> Language:
|
||||
colors = raw["colors"]
|
||||
|
||||
build = raw.get("build", {})
|
||||
run = raw["run"]
|
||||
return cls(
|
||||
directory=raw["directory"],
|
||||
|
||||
name=raw["name"],
|
||||
description=raw["description"],
|
||||
colors=Colors(
|
||||
fg=colors["foreground"],
|
||||
bg=colors["background"],
|
||||
),
|
||||
build_command=build.get("common", build.get(OS_KEY)),
|
||||
run_command=run.get("common", run.get(OS_KEY)),
|
||||
)
|
||||
|
||||
@property
|
||||
def assignments(self) -> dict[str, Assignment]:
|
||||
for assignment_dir in self.__directory.iterdir():
|
||||
if not assignment_dir.is_dir():
|
||||
continue
|
||||
|
||||
assignment_metadata = decode_toml((assignment_dir / "assignment.toml").read_text())
|
||||
assignment_metadata["directory"] = assignment_dir
|
||||
assignment_metadata["language"] = self
|
||||
|
||||
yield Assignment.from_raw(assignment_metadata)
|
||||
|
||||
|
||||
class Assignment:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
language: Language,
|
||||
directory: Path,
|
||||
|
||||
name: str,
|
||||
date: str,
|
||||
description: str,
|
||||
directions: str,
|
||||
) -> None:
|
||||
self.language = language
|
||||
self.__directory = directory
|
||||
|
||||
self.name = name
|
||||
self.date = date
|
||||
self.description = description
|
||||
self.directions = directions
|
||||
|
||||
@classmethod
|
||||
def from_raw(cls, raw: dict) -> Assignment:
|
||||
return cls(
|
||||
language=raw["language"],
|
||||
directory=raw["directory"],
|
||||
|
||||
name=raw["name"],
|
||||
date=raw["date"],
|
||||
description=raw["description"],
|
||||
directions=raw["directions"],
|
||||
)
|
||||
|
||||
def compile(self, *, quiet: bool = False) -> bool | None:
|
||||
"""None = not a compiled language"""
|
||||
if self.language.build_command is None:
|
||||
return None
|
||||
|
||||
return cmd(self.language.build_command + (QUIET_SUFFIX if quiet else "")) == 0
|
3
run.sh
Normal file
3
run.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/usr/bin/bash
|
||||
python3 -m pip install -U chalky > /dev/null
|
||||
python3 -m interactive_runner
|
|
@ -1,9 +1,8 @@
|
|||
name = "example"
|
||||
name = "Example"
|
||||
given-date = "3/11/2022"
|
||||
main-file = "example.c"
|
||||
description = "This is an example entry for C"
|
||||
directions = """
|
||||
Print "Example" 10 times.
|
||||
"""
|
||||
|
||||
[compile]
|
||||
main-file = "example.c"
|
||||
|
|
|
@ -2,15 +2,14 @@ name = "C"
|
|||
description = "C (pronounced like the letter c) is a general-purpose computer programming language"
|
||||
|
||||
[colors]
|
||||
foreground = 0x000000
|
||||
background = 0x00599d
|
||||
foreground = "ffffff"
|
||||
background = "00599d"
|
||||
|
||||
[build]
|
||||
common = "gcc -o {out-file} {main-file}"
|
||||
|
||||
[run]
|
||||
linux = "./{out-file}"
|
||||
windows = "{out-file}"
|
||||
common = "{}"
|
||||
|
||||
[dependencies]
|
||||
common = ["gcc"]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
name = "star-rating"
|
||||
name = "Star Rating"
|
||||
given-date = "30/10/2022"
|
||||
main-file = "star_rating.c"
|
||||
description = "A program that takes 21 reviews and calculates the average rating and prints some stars"
|
||||
directions = """
|
||||
1. Read in 21 user ratings for a movie.
|
||||
|
@ -26,6 +27,3 @@ star, e.g. 14.3% one star, 23.8% two star, etc.
|
|||
Display this to the screen in a similar manner
|
||||
to task 4.
|
||||
"""
|
||||
|
||||
[compile]
|
||||
main-file = "star_rating.c"
|
|
@ -1,9 +1,7 @@
|
|||
name = "example"
|
||||
given-date = "3/11/2022"
|
||||
main-file = "example.py"
|
||||
description = "This is an example entry for CPython"
|
||||
directions = """
|
||||
Print "Example" 10 times.
|
||||
"""
|
||||
|
||||
[compile]
|
||||
main-file = "example.py"
|
||||
|
|
|
@ -2,13 +2,13 @@ name = "CPython"
|
|||
description = "Python is a high-level, general-purpose programming language"
|
||||
|
||||
[colors]
|
||||
foreground = 0xfed140
|
||||
background = 0x3670a0
|
||||
foreground = "fed140"
|
||||
background = "3670a0"
|
||||
|
||||
[run]
|
||||
linux = "python3 {main-file}"
|
||||
windows = "py {main-file}"
|
||||
unix = "python3 {}"
|
||||
windows = "py {}"
|
||||
|
||||
[dependencies]
|
||||
linux = ["python3"]
|
||||
unix = ["python3"]
|
||||
windows = ["py"]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue