1 // Copyright Mario Kröplin 2021. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE_1_0.txt or copy at 4 // https://www.boost.org/LICENSE_1_0.txt) 5 6 module epsilon.main; 7 8 import epsilon.settings; 9 import io : Input, read; 10 import log; 11 import runtime; 12 import std.range; 13 import std.stdio; 14 15 void main(string[] args) 16 { 17 import core.stdc.stdlib : exit, EXIT_FAILURE, EXIT_SUCCESS; 18 import std.exception : ErrnoException; 19 import std.getopt : defaultGetoptPrinter, getopt, GetoptResult; 20 21 GetoptResult result; 22 Settings settings; 23 24 try 25 { 26 with (settings) 27 { 28 result = getopt(args, 29 "c", "Disable collapsing constant trees.", &c, 30 "g", "Generate only, do not compile.", &generate, 31 "p", "Parser ignores regular token marks at hyper-nonterminals.", &p, 32 "o", "Disable optimizing of variable storage in compiled compiler.", &o, 33 "r", "Disable reference counting in compiled compiler.", &r, 34 "space|s", "Compiled compiler uses space instead of newline as separator.", &space, 35 "verbose|v", "Print debug output.", &verbose, 36 "write|w", "Write compilation output as default.", &write, 37 "slag", "Generate SLAG evaluator.", &slag, 38 "sweep", "Generate single-sweep evaluator.", &sweep, 39 "soag", "Generate SOAG evaluator.", &soag, 40 "output-directory", "Write compiled compiler to directory.", &outputDirectory, 41 "offset", "Show error positions language-server friendly as offsets.", &offset, 42 ); 43 } 44 } 45 catch (Exception exception) 46 { 47 error!"%s"(exception.msg); 48 exit(EXIT_FAILURE); 49 } 50 if (result.helpWanted) 51 { 52 import std.path : baseName; 53 54 writefln!"Usage: %s [options] <file>..."(args.front.baseName); 55 writeln("Compile each Extended Affix Grammar file into a compiler."); 56 defaultGetoptPrinter("Options:", result.options); 57 exit(EXIT_SUCCESS); 58 } 59 60 with (settings) 61 { 62 if (verbose) 63 levels |= Level.trace; 64 65 if (!slag && !sweep && !soag) 66 { 67 // try all evaluators until one fits 68 slag = true; 69 sweep = true; 70 soag = true; 71 } 72 if (!outputDirectory.empty) 73 { 74 import std.file : mkdirRecurse; 75 76 mkdirRecurse(outputDirectory); 77 } 78 } 79 try 80 { 81 import std.typecons : No, Yes; 82 83 const offset = settings.offset ? Yes.offset : No.offset; 84 85 if (args.dropOne.empty) 86 compile(read("stdin", stdin, offset), settings); 87 88 foreach (arg; args.dropOne) 89 compile(read(arg, offset), settings); 90 } 91 catch (ErrnoException exception) 92 { 93 error!"%s"(exception.msg); 94 exit(EXIT_FAILURE); 95 } 96 catch (Exception exception) 97 { 98 exit(EXIT_FAILURE); 99 } 100 } 101 102 void compile(Input input, Settings settings) 103 { 104 import analyzer = epsilon.analyzer; 105 import EAG = epsilon.eag; 106 import ELL1Gen = epsilon.ell1gen; 107 import LexGen = epsilon.lexgen; 108 import Predicates = epsilon.predicates; 109 import SLAGGen = epsilon.slaggen; 110 import SOAGGen = epsilon.soag.soaggen; 111 import Sweep = epsilon.sweep; 112 import std.exception : enforce; 113 114 analyzer.Analyse(input); 115 116 enforce(analyzer.ErrorCounter == 0); 117 118 analyzer.Warnings; 119 Predicates.Check; 120 121 ELL1Gen.Test(settings); 122 123 enforce(!ELL1Gen.Error); 124 125 string[] fileNames; 126 bool success = false; 127 128 if (settings.slag) 129 { 130 SLAGGen.Test; 131 if (EAG.History & EAG.isSLAG) 132 { 133 fileNames = LexGen.Generate(settings) ~ fileNames; 134 fileNames = ELL1Gen.Generate(settings) ~ fileNames; 135 success = true; 136 } 137 } 138 if (!success && settings.sweep) 139 { 140 Sweep.Test(settings); 141 if (EAG.History & EAG.isSweep) 142 { 143 fileNames = LexGen.Generate(settings) ~ fileNames; 144 fileNames = Sweep.Generate(settings) ~ fileNames; 145 fileNames = ELL1Gen.GenerateParser(settings) ~ fileNames; 146 success = true; 147 } 148 } 149 if (!success && settings.soag) 150 { 151 fileNames = LexGen.Generate(settings) ~ fileNames; 152 fileNames = SOAGGen.Generate(settings) ~ fileNames; 153 if (settings.verbose) 154 { 155 import protocol = epsilon.soag.protocol; 156 157 protocol.WriteRulesL4; 158 protocol.WriteSyms; 159 } 160 fileNames = ELL1Gen.GenerateParser(settings) ~ fileNames; 161 success = true; 162 } 163 164 enforce(success); 165 166 if (!fileNames.empty && !settings.generate) 167 build(fileNames, settings.outputDirectory); 168 } 169 170 void build(string[] fileNames, string outputDirectory) 171 { 172 import core.stdc.stdlib : exit; 173 import std.format : format; 174 import std.path : stripExtension; 175 import std.process : spawnProcess, wait; 176 import std..string : join; 177 178 auto args = "dmd" ~ fileNames ~ "-g" ~ "include/runtime.d" 179 ~ "src/io.d" ~ "src/log.d" ~ "src/epsilon/soag/listacks.d"; 180 181 if (!outputDirectory.empty) 182 { 183 args ~= format!"-od=%s"(outputDirectory); 184 args ~= format!"-of=%s"(fileNames.front.stripExtension); 185 } 186 writefln!"%s"(args.join(' ')); 187 188 auto pid = spawnProcess(args); 189 const status = wait(pid); 190 191 if (status) 192 exit(status); 193 }