pascalabcnet/pabcnetc_clear/ConsoleCompiler.cs
Александр Земляк cb09e2340f
Удаление supportedProjectFiles из Compiler и настройка использования SPython в проектах (#3374)
* Set up creating SPython projects

* Delete SupportedSourceFile class in Compiler

* Implement GetUnitTemplate function for SPython
2026-01-16 20:13:20 +03:00

275 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using PascalABCCompiler.Errors;
using System.IO;
using System.Linq;
using System.Globalization;
namespace PascalABCCompiler
{
/*class mc<T>
{
public static int meth()
{
return 3;
}
}*/
class ConsoleCompiler
{
private static string StringsPrefix = "PABCNETC_";
public static PascalABCCompiler.Compiler Compiler=null;
public static string FileName = "";
public static CompilerOptions.OutputType outputType;
public static DateTime StartTime;
private static string StringResourcesGet(string Key)
{
return StringResources.Get(StringsPrefix + Key);
}
/*public static void ShowConnectedParsers()
{
if (LanguageProvider.Instance.Languages.Count > 0)
{
Console.Write(StringResourcesGet("CONNECTED_PARSERS"));
foreach (ILanguage lang in LanguageProvider.Instance.Languages)
Console.Write(FormatTools.LanguageAndExtensionsFormatted(lang.Name, lang.FilesExtensions)+"; ");
Console.WriteLine();
}
}*/
public static bool CheckAndSplitDirective(string directive, out string name, out string value)
{
System.Diagnostics.Debug.Assert(directive[0] == '/');
directive = directive.Remove(0, 1);
name = null;
value = null;
var ss = directive.Split( new[]{':'}, 2);
name = ss[0].ToLower();
if (ss.Length > 1)
{
value = ss[1].Trim();
}
return true;
}
public static bool ApplyDirective(string name, string value, CompilerOptions co)
{
switch (name)
{
case "debug":
switch (value)
{
case "0":
co.Debug = false;
co.Optimise = true;
return true;
case "1":
co.Debug = true;
return true;
default:
Console.WriteLine("Bad value in 'Debug' directive '{0}'. Acceptable values are 0 or 1", value);
return false;
}
case "define":
co.ForceDefines.Add(value);
return true;
case "output":
if (!Compiler.CheckPathValid(value))
return false;
co.OutputFileName = Path.GetFileName(value);
if (Path.IsPathRooted(value))
{
co.OutputDirectory = Path.GetDirectoryName(value);
}
return true;
case "searchdir":
if (!Directory.Exists(value))
{
Console.WriteLine($"SearchDir \"{value}\" not found relative to \"{Environment.CurrentDirectory}\"");
return false;
}
co.SearchDirectories.Insert(0, value); // .Insert, чтобы определённые пользователем папки имели бОльший приоритет, чем стандартная
return true;
case "locale":
co.Locale = value;
return true;
default:
Console.WriteLine("No such directive name: '{0}'", name);
return false;
}
}
public static bool CheckAndApplyDirective(string directive, CompilerOptions co)
{
string name, value;
var b = CheckAndSplitDirective(directive,out name, out value);
if (!b)
return false;
b = ApplyDirective(name, value, co);
return b;
}
public static void OutputHelp()
{
Console.WriteLine("Command line: ");
Console.WriteLine("pabcnetcclear /directive1:value1 /directive2:value2 ... [inputfile]\n");
Console.WriteLine("Available directives:");
//Console.WriteLine(" /Help /H /?"); - запретил - конкурирует с pabcnetcclear /w/a.pas
Console.WriteLine(" /Debug:<0/1>");
Console.WriteLine(" /Define:<name>");
Console.WriteLine(" /Output:<[path\\]name>");
Console.WriteLine(" /SearchDir:<path>");
Console.WriteLine(" /Locale:<locale>");
Console.WriteLine(" /Version:");
Console.WriteLine();
Console.WriteLine("/Help show this message");
Console.WriteLine("/Output:<[path\\]name> compile into an executable called \"name\" and save it in \"path\" directory");
Console.WriteLine("/Debug:0 generates code with all .NET optimizations");
Console.WriteLine("/SearchDir:<path> add \"path\" to list of standart unit search directories. Last added paths would be searched first");
Console.WriteLine("/Locale:<locale> set locale of main thread of compiled program executable");
Console.WriteLine("/Version: outputs PascalABC.NET version");
}
public static int Main(string[] args)
{
// SSM 18.03.19 Делаю параметры командной строки. Формат: pabcnetc.exe /Dir1=value1 /Dir2=value2 ... fname dir
// dir - это пережиток старого - так можно было задавать каталог раньше. Пока - увы - пусть останется
// Пока сделаю только директивы /Help /H /? и /Debug=0(1)
// Имя директивы - это одно слово. Равенства может не быть - тогда value директивы равно null
// Вычленяем первое равенство и делим директиву: до него - name, после него - value. Если name или value - пустые строки, то ошибка
// 2022 г. К сожалению, директива с / конкурирует с именами каталогов в Linux. Надо писать новый консольный компилятор
DateTime ldt = DateTime.Now;
PascalABCCompiler.StringResourcesLanguage.LoadDefaultConfig();
// загрузка всех парсеров и других составляющих языков EVA
Languages.Integration.LanguageIntegrator.LoadAllLanguages();
Compiler = new PascalABCCompiler.Compiler(null, null);
Compiler.InternalDebug.SkipPCUErrors = false;
CultureInfo ci = CultureInfo.InstalledUICulture;
if (StringResourcesLanguage.CurrentTwoLetterISO == "ru" && ci.TwoLetterISOLanguageName != "ru")
StringResourcesLanguage.CurrentLanguageName = StringResourcesLanguage.AccessibleLanguages[StringResourcesLanguage.TwoLetterISOLanguages.IndexOf("en")];
else
StringResourcesLanguage.CurrentLanguageName = StringResourcesLanguage.AccessibleLanguages[StringResourcesLanguage.TwoLetterISOLanguages.IndexOf(StringResourcesLanguage.CurrentTwoLetterISO)];
//Console.WriteLine("OK {0}ms", (DateTime.Now - ldt).TotalMilliseconds);
ldt = DateTime.Now;
//ShowConnectedParsers();
var n = args.Length;
if (n == 0)
{
//Console.WriteLine(StringResourcesGet("COMMANDLINEISABSENT"));
OutputHelp();
return 2;
}
// /W/a.pas расценивается как директива. Нужен более сложный паттерн: начинается с \буквы:
//var n1 = args.TakeWhile(a => a[0] == '/').Count(); // количество директив
// Поменяем: SSM 14/12/21
var n1 = args.TakeWhile(a => System.Text.RegularExpressions.Regex.IsMatch(a, @"^/\w+:")).Count(); // количество директив
if (n1<n-2)
{
Console.WriteLine("Error in argument {0}", args[n1+2]);
Console.WriteLine("Command line cannot contain any arguments after filename '{0}' and outdirname '{1}'", args[n1], args[n1 + 1]);
return 4;
}
if (n1 == n) // только директивы
{
string name, value;
var b = CheckAndSplitDirective(args[0], out name, out value);
if (!b) // Сообщение уже будет выдано
return 4;
switch (name)
{
case "help":
case "h":
case "?":
OutputHelp();
return 0;
case "version":
Console.WriteLine(PascalABCCompiler.Compiler.Version);
return 0;
default:
Console.WriteLine("Filename is absent. Nothing to compile");
return 4;
}
}
FileName = args[n1]; // следующий аргумент за директивами - имя файла
if (!File.Exists(FileName))
{
Console.WriteLine(StringResourcesGet("FILEISABSENT{0}"), "'" + FileName + "'");
return 3;
}
outputType = CompilerOptions.OutputType.ConsoleApplicaton;
CompilerOptions co = new CompilerOptions(FileName, outputType);
if (FileName.ToLower().EndsWith(StringConstants.platformProjectExtension))
co.ProjectCompiled = true;
if (n1 == n - 1)
//co.OutputDirectory = ""
;
else co.OutputDirectory = args[n - 1];
co.Rebuild = false;
co.Debug = false;
co.UseDllForSystemUnits = false;
for (var i = 0; i < n1; i++)
{
var b = CheckAndApplyDirective(args[i], co);
if (!b)
return 4;
}
bool success = true;
if (Compiler.Compile(co) != null)
Console.WriteLine("OK");
else
{
Console.WriteLine(StringResourcesGet("COMPILEERRORS"));
success = false;
}
// Console.WriteLine("OK {0}ms", (DateTime.Now - ldt).TotalMilliseconds); /////
for (int i = 0; i < Compiler.ErrorsList.Count; i++)
{
if (Compiler.ErrorsList[i] is LocatedError)
{
SourceLocation sl;
if ((sl = (Compiler.ErrorsList[i] as LocatedError).SourceLocation) != null)
Console.WriteLine(string.Format("[{0},{1}] {2}: {3}", sl.BeginPosition.Line, sl.BeginPosition.Column, Path.GetFileName(sl.FileName), Compiler.ErrorsList[i].Message));
else
Console.WriteLine(Compiler.ErrorsList[i]);
}
else
{
Console.WriteLine(Compiler.ErrorsList[i].Message);
Console.WriteLine(Compiler.ErrorsList[i].StackTrace);
}
break; // выйти после первой же ошибки
}
if (success)
return 0;
else return 1;
}
}
}