1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.mal_lang.compiler;
18
19 import java.io.IOException;
20 import java.nio.charset.StandardCharsets;
21 import java.nio.file.AccessDeniedException;
22 import java.nio.file.Files;
23 import java.nio.file.NoSuchFileException;
24 import java.nio.file.NotDirectoryException;
25 import java.nio.file.Path;
26 import java.util.LinkedHashMap;
27 import java.util.Map;
28 import java.util.concurrent.Callable;
29 import org.fusesource.jansi.AnsiConsole;
30 import org.mal_lang.langspec.Utils;
31 import org.mal_lang.langspec.io.LangWriter;
32 import org.mal_lang.lib.Analyzer;
33 import org.mal_lang.lib.CompilerException;
34 import org.mal_lang.lib.LangConverter;
35 import org.mal_lang.lib.Parser;
36 import picocli.CommandLine;
37 import picocli.CommandLine.Command;
38 import picocli.CommandLine.Option;
39 import picocli.CommandLine.Parameters;
40
41
42
43
44
45
46 @Command(
47 name = "malc",
48 versionProvider = ManifestVersionProvider.class,
49 mixinStandardHelpOptions = true,
50 description = {"A compiler for the Meta Attack Language."},
51 sortOptions = false)
52 public class MalCompiler implements Callable<Integer> {
53 @Parameters(index = "0", paramLabel = "file", description = "The MAL specification to compile")
54 private Path input;
55
56 @Option(
57 names = {"-i", "--icons"},
58 paramLabel = "<dir>",
59 description = "Icons directory")
60 private Path icons;
61
62 @Option(
63 names = {"-l", "--license"},
64 paramLabel = "<file>",
65 description = "License file")
66 private Path license;
67
68 @Option(
69 names = {"-n", "--notice"},
70 paramLabel = "<file>",
71 description = "Notice file")
72 private Path notice;
73
74 @Option(
75 names = {"-o", "--output"},
76 paramLabel = "<file>",
77 description = "Write output to <file>")
78 private Path output;
79
80 @Option(
81 names = {"-v", "--verbose"},
82 description = "Print verbose output")
83 private boolean verbose;
84
85 @Option(
86 names = {"-d", "--debug"},
87 description = "Print debug output")
88 private boolean debug;
89
90 private final Map<String, byte[]> svgIcons = new LinkedHashMap<>();
91 private final Map<String, byte[]> pngIcons = new LinkedHashMap<>();
92 private String licenseString = null;
93 private String noticeString = null;
94
95 private void readIcons() throws IOException {
96 if (this.icons != null) {
97 try (var directoryStream = Files.newDirectoryStream(this.icons)) {
98 for (var entry : directoryStream) {
99 if (Files.isDirectory(entry)) {
100 continue;
101 }
102 if (entry.toString().endsWith(".svg")) {
103 var assetName = entry.getFileName().toString();
104 assetName = assetName.substring(0, assetName.length() - ".svg".length());
105 if (Utils.isIdentifier(assetName)) {
106 this.svgIcons.put(assetName, Files.readAllBytes(entry));
107 }
108 } else if (entry.toString().endsWith(".png")) {
109 var assetName = entry.getFileName().toString();
110 assetName = assetName.substring(0, assetName.length() - ".png".length());
111 if (Utils.isIdentifier(assetName)) {
112 this.pngIcons.put(assetName, Files.readAllBytes(entry));
113 }
114 }
115 }
116 }
117 }
118 }
119
120 @Override
121 public Integer call() throws Exception {
122 try {
123 this.readIcons();
124 if (this.license != null) {
125 this.licenseString = Files.readString(license, StandardCharsets.UTF_8);
126 }
127 if (this.notice != null) {
128 this.noticeString = Files.readString(notice, StandardCharsets.UTF_8);
129 }
130 var ast = Parser.parse(this.input.toFile(), this.verbose, this.debug);
131 Analyzer.analyze(ast, this.verbose, this.debug);
132 var lang =
133 LangConverter.convert(
134 ast,
135 this.verbose,
136 this.debug,
137 this.svgIcons,
138 this.pngIcons,
139 this.licenseString,
140 this.noticeString);
141 if (this.output == null) {
142 this.output =
143 Path.of(String.format("%s-%s.mar", lang.getDefine("id"), lang.getDefine("version")));
144 }
145 try (var out = Files.newOutputStream(this.output);
146 var writer = new LangWriter(out)) {
147 writer.write(lang);
148 }
149 } catch (AccessDeniedException e) {
150 System.err.println(String.format("%s: Permission denied", e.getFile()));
151 return 1;
152 } catch (NoSuchFileException e) {
153 System.err.println(String.format("%s: No such file or directory", e.getFile()));
154 return 1;
155 } catch (NotDirectoryException e) {
156 System.err.println(String.format("%s: Not a directory", e.getFile()));
157 return 1;
158 } catch (IOException e) {
159 System.err.println(e.getMessage());
160 return 1;
161 } catch (CompilerException e) {
162 return 1;
163 }
164 return 0;
165 }
166
167 public static void main(String... args) {
168 AnsiConsole.systemInstall();
169 int exitCode = new CommandLine(new MalCompiler()).execute(args);
170 AnsiConsole.systemUninstall();
171 System.exit(exitCode);
172 }
173 }