From 65ef948244bd6315e56e8f1f7ef278810d40c509 Mon Sep 17 00:00:00 2001 From: Wyrdix Date: Tue, 30 Jun 2026 21:35:54 +0200 Subject: [PATCH] Add Python primitives generation --- lib/resources/langages/python/Remote.py | 50 +++++++ src/plm/core/lang/LangPython.java | 130 ++++++++++++++++++ .../core/lang/primitives/CodeCreation.java | 28 +++- 3 files changed, 202 insertions(+), 6 deletions(-) create mode 100644 lib/resources/langages/python/Remote.py diff --git a/lib/resources/langages/python/Remote.py b/lib/resources/langages/python/Remote.py new file mode 100644 index 000000000..454a1c4a6 --- /dev/null +++ b/lib/resources/langages/python/Remote.py @@ -0,0 +1,50 @@ +import sys + +debug_fd = None + +def get_answer_line(): + line = sys.stdin.readline() + if line == "": + sys.exit(1) + + if debug_fd: + print(f"Answer: {line.rstrip()}", file=debug_fd) + debug_fd.flush() + + return line.rstrip("\r\n") + + +def get_answer_int(): + return int(get_answer_line()) + + +def get_answer_double(): + return float(get_answer_line()) + + +def get_answer_string(): + return get_answer_line() + + +def get_answer_char(): + line = get_answer_line() + return line[0] if line else "" + + +def send_command(format_string, *args): + command = format_string % args + + if debug_fd: + print(f"Command from C world: '{command}'", file=debug_fd) + debug_fd.flush() + + print(command, file=sys.stderr) + sys.stderr.flush() + + +# BEGIN UTILS FUNCTIONS + +def int2str(nb): + return str(nb) + +# END UTILS FUNCTIONS \ No newline at end of file diff --git a/src/plm/core/lang/LangPython.java b/src/plm/core/lang/LangPython.java index 4677c1ed9..bb47a65b5 100644 --- a/src/plm/core/lang/LangPython.java +++ b/src/plm/core/lang/LangPython.java @@ -3,11 +3,23 @@ import javax.script.ScriptEngineManager; import javax.script.ScriptException; import org.python.core.PyException; +import plm.core.lang.primitives.CommandArgumentType; +import plm.core.lang.primitives.ExternalPrimitiveLanguage; +import plm.core.lang.primitives.PrimitiveMethod; +import plm.core.lang.primitives.PrimitiveParameter; import plm.core.model.Game; import plm.core.model.lesson.RunOutcome; import plm.core.ui.ResourcesCache; import plm.universe.Entity; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + public class LangPython extends ScriptingLanguage { public LangPython() { @@ -159,4 +171,122 @@ public boolean handleLangException(ScriptException e, Entity ent, RunOutcome pro return true; // That was indeed a Python exception } + + public static class LangPythonExternalPrimitiveGenerator implements ExternalPrimitiveLanguage { + + String getLanguageType(CommandArgumentType type) { + if (type == CommandArgumentType.COLOR) return "int"; + if (type == CommandArgumentType.DIRECTION) return "int"; + if (type == CommandArgumentType.DOUBLE) return "float"; + if (type == CommandArgumentType.INT) return "int"; + if (type == CommandArgumentType.STRING) return "str"; + if (type == CommandArgumentType.CHAR) return "str"; + if (type == CommandArgumentType.BOOLEAN) + return "bool"; + + throw new IllegalStateException("Unknown type: " + type); + } + + String getTypeDeclaration(CommandArgumentType type) { + if (type == CommandArgumentType.DIRECTION) { + return "NORTH = 0\n" + + "EAST = 1\n" + + "SOUTH = 2\n" + + "WEST = 3\n"; + } + if (type == CommandArgumentType.COLOR) { + return "white = 0\n" + + "black = 1\n" + + "blue = 2\n" + + "cyan = 3\n" + + "darkGray = 4\n" + + "gray = 5\n" + + "green = 6\n" + + "lightGray = 7\n" + + "magenta = 8\n" + + "orange = 9\n" + + "pink = 10\n" + + "red = 11\n" + + "yellow = 12\n"; + } + return ""; + } + + String getParameter(PrimitiveParameter parameter) { + return parameter.name()+": "+getLanguageType(parameter.type()); + } + + String getPrototype(PrimitiveMethod method) { + String name = method.name(); + List parameters = method.parameters(); + CommandArgumentType output = method.output(); + + + final String outputString = Optional.ofNullable(output).map(this::getLanguageType).orElse("None"); + + return "def " + name + "(" + parameters.stream().map(this::getParameter).collect(Collectors.joining(", ")) + ") -> "+outputString+":"; + } + + String getReturning(CommandArgumentType type) { + if (type == null) + return ""; + + if (type == CommandArgumentType.STRING) return "get_answer_string()"; + if (type == CommandArgumentType.DOUBLE) return "get_answer_double()"; + if (type == CommandArgumentType.CHAR) return "get_answer_char()"; + if (type == CommandArgumentType.COLOR) return "get_answer_int()"; + if (type == CommandArgumentType.DIRECTION) return "get_answer_int()"; + if (type == CommandArgumentType.INT) return "get_answer_int()"; + if (type == CommandArgumentType.BOOLEAN) + return "get_answer_int()"; + + throw new IllegalStateException("Unknown type: " + type); + } + + String getTemplatingForType(CommandArgumentType type) { + if (type == CommandArgumentType.STRING) return "%s"; + if (type == CommandArgumentType.DOUBLE) return "%f"; + if (type == CommandArgumentType.CHAR) return "%s"; + if (type == CommandArgumentType.COLOR) return "%d"; + if (type == CommandArgumentType.DIRECTION) return "%d"; + if (type == CommandArgumentType.INT) return "%d"; + if (type == CommandArgumentType.BOOLEAN) + return "%d"; + + throw new IllegalStateException("Unknown type: " + type); + } + + String getImplementation(PrimitiveMethod method) { + String prototype = getPrototype(method); + + int id = method.id(); + String name = method.name(); + String formats = method.parameters().stream().map(PrimitiveParameter::type) + .map(this::getTemplatingForType).map(s -> s + " ").collect(Collectors.joining()); + + String command = "\tsend_command(\"" + id + " " + + formats + + name + + "\"" + method.parameters().stream().map(PrimitiveParameter::name).map(s -> ", "+s).collect(Collectors.joining()) + ")"; + + String returning = method.output() != null ? "\treturn " + getReturning(method.output()) : ""; + + return prototype + "\n" + command + "\n" + returning + "\n"; + } + + @Override + public void generate(File folder, String name, List methods) throws IOException { + Set> involved = ExternalPrimitiveLanguage.involved(methods); + + final String type_declarations = involved.stream().map(this::getTypeDeclaration) + .filter(o -> !o.isBlank()).collect(Collectors.joining("\n\n")); + + final String implementations = methods.stream().map(this::getImplementation).collect(Collectors.joining("\n\n")); + + final String code = ("from Remote import *\n\n" + type_declarations + implementations).replace("\t", " ".repeat(4)); + + // System.err.println("XXX Generating "+folder+name+".c"); + Files.writeString(new File(folder, name + ".py").toPath(), code); + } + } } diff --git a/src/plm/core/lang/primitives/CodeCreation.java b/src/plm/core/lang/primitives/CodeCreation.java index 08acaea92..1fec88f8d 100644 --- a/src/plm/core/lang/primitives/CodeCreation.java +++ b/src/plm/core/lang/primitives/CodeCreation.java @@ -7,6 +7,7 @@ import lessons.sort.pancake.universe.PancakeEntity; import org.reflections.Reflections; import plm.core.lang.LangC; +import plm.core.lang.LangPython; import plm.universe.Entity; import plm.universe.bugglequest.AbstractBuggle; import plm.universe.sort.SortingEntity; @@ -19,9 +20,7 @@ public class CodeCreation { public static void main(String[] args) throws IOException { - File folder = new File("target/classes/resources/langages/c"); - - LangC.LangCExternalPrimitiveGenerator generator = new LangC.LangCExternalPrimitiveGenerator(); + File folder = new File("target/classes/resources/langages/"); Map> remoteMap = Map.of( "RemoteBuggle", AbstractBuggle.class, @@ -33,6 +32,25 @@ public static void main(String[] args) throws IOException { "RemoteFlag", DutchFlagEntity.class ); + LangC.LangCExternalPrimitiveGenerator langCGenerator = new LangC.LangCExternalPrimitiveGenerator(); + File cFolder = new File(folder, "c"); + for (Map.Entry> entry : remoteMap.entrySet()) { + String name = entry.getKey(); + Class clazz = entry.getValue(); + + Map list = null; + try { + list = PrimitiveRegistration.getMaximalPrimitiveForEntity(clazz); + } catch (Exception e) { + throw new RuntimeException(e); + } + langCGenerator.generate(cFolder, name, list.values().stream().toList()); + Files.move(new File(cFolder, name+".h"), new File(cFolder,"include/"+name+".h")); + Files.move(new File(cFolder, name+".c"), new File(cFolder,"src/"+name+".c")); + } + + LangPython.LangPythonExternalPrimitiveGenerator langPythonGenerator = new LangPython.LangPythonExternalPrimitiveGenerator(); + File pythonFolder = new File(folder, "python"); for (Map.Entry> entry : remoteMap.entrySet()) { String name = entry.getKey(); Class clazz = entry.getValue(); @@ -43,9 +61,7 @@ public static void main(String[] args) throws IOException { } catch (Exception e) { throw new RuntimeException(e); } - generator.generate(folder, name, list.values().stream().toList()); - Files.move(new File(folder, name+".h"), new File(folder,"include/"+name+".h")); - Files.move(new File(folder, name+".c"), new File(folder,"src/"+name+".c")); + langPythonGenerator.generate(pythonFolder, name, list.values().stream().toList()); } } }