public void Test_GenerateTokens_CanTraverseEntireFileAndThrowErrors(string content) { StreamReader FakeReader = CreateFakeReader(content, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); Assert.IsTrue(Tokeniser.HasError); }
public void Test_GenerateTokens_CanNotGenrateInvalidRanges() { string content = "a is 0...5"; StreamReader FakeReader = CreateFakeReader(content, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); Assert.IsTrue(Tokeniser.HasError); }
public void Test_GenerateTokens_CanTraverseEntireFileWithNoErrorsAndCorrentAmountOfTokens(string content, int expectedAmountOfTokens) { StreamReader FakeReader = CreateFakeReader(content, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); Assert.AreEqual(expectedAmountOfTokens, tokeniser.Tokens.Count, "tokeniser did not generate the correct amount of tokens."); }
public void Test_GenerateTokens_ThrowsExceptionOnInvalidMultilineComment() { string content = "#< comment"; StreamReader FakeReader = CreateFakeReader(content, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); Assert.IsTrue(Tokeniser.HasError); }
public void Test_GenerateTokens_TokenListIsEmpty() { string content = ""; StreamReader FakeReader = CreateFakeReader(content, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); Assert.AreEqual(0, tokeniser.Tokens.Count, "TokenList had the wrong amount of tokens"); }
public void Test_GenerateTokens_GeneratesMultiLineCommentToken() { string content = "#< comment >#"; StreamReader FakeReader = CreateFakeReader(content, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); Assert.AreEqual(TokenType.MULT_COMNT, tokeniser.Tokens.First.Value.Type, "The tokeniser did not recognise the comment as a comment"); }
public void Test_GenerateTokens_CanScanMultilineComments() { string content = "#< comment >#"; StreamReader FakeReader = CreateFakeReader(content, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); Assert.AreEqual(1, tokeniser.Tokens.Count, "The tokeniser did not recognise a comment"); }
public void Test_GenerateTokens_TokenListNotEmpty() { string content = "a is 4"; StreamReader FakeReader = CreateFakeReader(content, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); Assert.AreEqual(3, tokeniser.Tokens.Count, "TokenList generated the wrong amound of tokens. Should contain elements more than just PROG and EOF"); }
public void Test_ASTHelper_Constructor(string content) { StreamReader FakeReader = CreateFakeReader(content, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); List <ScannerToken> tokens = tokeniser.Tokens.ToList(); Parser.Parser parser = new Parser.Parser(tokens); parser.Parse(out nowhere); Assert.IsFalse(Parser.Parser.HasError, "The parser encountered an error"); }
public void Test_GenerateTokens_CanGenerateRanges() { string content = "a is 0..5"; StreamReader FakeReader = CreateFakeReader(content, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); Assert.AreEqual(5, tokeniser.Tokens.Count, $"The tokeniser found the wrong amount of tokens."); }
public void Test_AssignmentNode_NodeHasNoOperator() { StreamReader reader = CreateFakeReader(program2); Tokeniser tokeniser = new Tokeniser(reader); tokeniser.GenerateTokens(); Parser.Parser parser = new Parser.Parser(tokeniser.Tokens.ToList()); parser.Parse(out nowhere); Assert.IsFalse(Parser.Parser.HasError, "Parser encountered an error state:\n\n" + nowhere); parser.Root.Accept(new TypeChecker(new List <string>() { "3", "5", "6", "9", "10", "11" })); Assert.IsNull(((AssignmentNode)parser.Root.Statements[0]).Operator, "Operator was not null for assignment node"); }
public void Test_TypeChecker_CheckHasNoErrors(string program) { StreamReader reader = CreateFakeReader(program); Tokeniser tokeniser = new Tokeniser(reader); tokeniser.GenerateTokens(); Parser.Parser parser = new Parser.Parser(tokeniser.Tokens.ToList()); parser.Parse(out nowhere); Assert.IsFalse(Parser.Parser.HasError, "Parser encountered an error state:\n\n" + nowhere); parser.Root.Accept(new TypeChecker(new List <string>() { "3", "5", "6", "9", "10", "11" })); Assert.IsFalse(TypeChecker.HasError, "Typechecker encountered an error."); }
public void Test_ASTHelper_Assign_2(string test) { StreamReader FakeReader = CreateFakeReader(test, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); List <ScannerToken> tokens = tokeniser.Tokens.ToList(); Parser.Parser parser = new Parser.Parser(tokens); parser.Parse(out nowhere); if (Parser.Parser.HasError) { Assert.Fail(); } parser.Root.Accept(new PrettyPrinter()); }
public void Test_TypeChecker_CanThrowExceptions(string program) { StreamReader reader = CreateFakeReader(program); Tokeniser tokeniser = new Tokeniser(reader); tokeniser.GenerateTokens(); Parser.Parser parser = new Parser.Parser(tokeniser.Tokens.ToList()); parser.Parse(out nowhere); Assert.IsFalse(Parser.Parser.HasError, "Parser encountered an error state:\n\n" + nowhere); try { parser.Root.Accept(new TypeChecker(new List <string>() { "3", "5", "6", "9", "10", "11" })); } catch { } Assert.IsTrue(TypeChecker.HasError, "The error was not caught"); }
public void Test_Program(string program) { StreamReader FakeReader = CreateFakeReader(program, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); List <ScannerToken> tokens = tokeniser.Tokens.ToList(); Parser.Parser parser = new Parser.Parser(tokens); parser.Parse(out nowhere); if (Parser.Parser.HasError) { Assert.Fail(); } parser.Root.Accept(new TypeChecker(new List <string>() { "3", "5", "6", "9", "10", "11" })); CodeGenerationVisitor codeGenerationVisitor = new CodeGenerationVisitor("Codegen_output.cpp", new List <string>()); parser.Root.Accept(codeGenerationVisitor); }
public void Test_CodeGenVisitor_pin_fail() { StreamReader FakeReader = CreateFakeReader(pin_fail, Encoding.UTF8); Tokeniser tokeniser = new Tokeniser(FakeReader); tokeniser.GenerateTokens(); List <ScannerToken> tokens = tokeniser.Tokens.ToList(); Parser.Parser parser = new Parser.Parser(tokens); parser.Parse(out dbg); if (Parser.Parser.HasError) { Assert.Fail("The parser encountered an error\n\n" + dbg); } parser.Root.Accept(new TypeChecker(new List <string>() { "3", "5", "6", "9", "10", "11" })); Assert.IsFalse(TypeChecker.HasError, "Typechecker visitor encountered an error"); CodeGenerationVisitor codeGenerationVisitor = new CodeGenerationVisitor("Codegen_output.cpp", new List <string>()); parser.Root.Accept(codeGenerationVisitor); Assert.IsTrue(CodeGenerationVisitor.HasError, "Code gen visitor encountered an error"); }
/// <summary> /// The entry point of the compiler. /// Here the user must provide a set of compiler flags, in order for the compiler to produce a satisfying product. /// The user must provide a filepath to the file they wish to compile. Otherwise the compiler will halt and exit with an exit code of 1. /// Compiler flags can be provided to activate additional functionality. /// **Compiler flags** /// `-d` or `--dryrun` Runs the compiler without producing an output. /// `-o` or `--output` Tells the compiler not to write to the Arduino, and instead produce a file. /// `-v` or `--verbose` Prints additional information when compiling. /// `-b` or `--boilerplate` Generates a boilerplate file for your code. /// `-l` or `--logfile` (Must be followed by a file path) Prints additional information when compiling. /// `-p` or `--port` (Must be followed by a port number) Specifies the port to upload to. /// `-a` or `--arduino` (Must be followed by an Arduino model) Specifies the arduino model you're uploading to. (Default: UNO) /// `-pr` or `--proc` (Must be followed by a valid processor) Specifies the arduino processor you're uploading to. (Default: atmega328p) /// `-pp` or `--prettyprinter` Print the abstract syntax tree. /// **Compiler exit code** /// `0` Compilation finished with no errors. /// `1` A file path was not provided to the compiler for compilation. /// `20` The file provided was not encoded as a UTF-8 file. /// `5` An error was encountered while scanning the input program. This is usually caused by an unclosed string, comment, or parenthesis. /// `4` An error was encountered in the parser. This is usually due to an invalidly structured program. /// `3` An error was encountered in the type checker. This happens when two types are mismatched, either on assignment or within an expression. Furthermore, this can be caused by not defining a called function, or a function being defined multiple times. /// `2` This error is encountered when the code generator can not find the output file for the intermediate representation code. /// `23` This error is encountered when the dryrun flag is invoked, but the compiler can not find the output file for the intermediate representation code. /// </summary> /// <param name="args">The arguments passed to the compiler from the terminal.</param> /// <returns>An exit code representing the state the compiler exited in.</returns> public static int Main(string[] args) { Stopwatch timer = new Stopwatch(); timer.Start(); options = ParseOptions(args); verbosePrinter = new VerbosePrinter(options); if (options?.InputFile == null) { Help(); verbosePrinter.Error("Input file was not specified."); return(1); } if (options.Boilerpate) { using (StreamReader reader = new StreamReader(AppContext.BaseDirectory + "/boilerplate")) { verbosePrinter.Info("Reading boilerplate template"); string content = reader.ReadToEnd(); using (StreamWriter writer = new StreamWriter(options.InputFile)) { verbosePrinter.Info("Writing boilerplate template to file"); writer.Write(content); } } verbosePrinter.Info("Done."); return(0); } verbosePrinter.Info("Initialising file."); using (StreamReader reader = new StreamReader(options.InputFile)) { try { verbosePrinter.Info("Checking encoding..."); FileChecker.CheckEncoding(reader); verbosePrinter.Info($" Encoding was {reader.CurrentEncoding}."); } catch (EncodingNotSupportedException e) { verbosePrinter.Error("File not encoded correctly."); Console.Error.WriteLine(e.Message); return(20); } Tokeniser tokeniser = new Tokeniser(reader); verbosePrinter.Info("Generating tokens..."); tokeniser.GenerateTokens(); if (Tokeniser.HasError) { verbosePrinter.Error("Encountered syntax errors. Stopping."); return(5); } verbosePrinter.Info($" Generated {tokeniser.Tokens.Count} tokens."); if (options.Verbose) { foreach (var token in tokeniser.Tokens) { verbosePrinter.Info(token); } } verbosePrinter.Info("Generating parse table"); List <ScannerToken> tokens = tokeniser.Tokens.ToList(); Parser.Parser parser = new Parser.Parser(tokens); string debugMessage = ""; parser.Parse(out debugMessage); verbosePrinter.Info(debugMessage); if (Parser.Parser.HasError) { verbosePrinter.Error("Encountered an error state in the parser. Stopping."); return(4); } if (options.PrettyPrinter) { parser.Root.Accept(new PrettyPrinter()); } try { parser.Root.Accept(new TypeChecker(GetPWMSet())); } catch { verbosePrinter.Error("Encountered an error in the type checker. Stopping."); return(3); } if (TypeChecker.HasError) { verbosePrinter.Error("Encountered an error in the type checker. Stopping."); return(3); } string path = AppContext.BaseDirectory; try { if (File.Exists($"{path}PrecompiledBinaries/tmp/sketch/output.cpp")) { File.Delete($"{path}PrecompiledBinaries/tmp/sketch/output.cpp"); } parser.Root.Accept(new CodeGenerationVisitor($"{path}PrecompiledBinaries/tmp/sketch/output.cpp", GetPWMSet())); } catch (FileNotFoundException e) { verbosePrinter.Error("Encountered an error in code generation. Stopping."); Console.Error.WriteLine(e.Message); return(2); } if (CodeGenerationVisitor.HasError) { return(10); } if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) { Console.WriteLine("We're on Linux!"); if (options.Port == "COM0") { Console.Error.WriteLine($"Error: No Port Provided. The compiler will try to find one available."); string[] devices = "ls /dev/tty*".Bash().Split("\n"); if (devices.Any(str => str.Contains("ACM"))) { options.Port = devices.First(str => str.Contains("ACM")); } } path = path.Replace(" ", "\\ "); $"chmod -R a+x {path}".Bash(); $"{path}PrecompiledBinaries/hardware/tools/avr/bin/avr-g++ {(options.Verbose ? "-v" : "")} -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu={options.Processor} -DF_CPU=16000000L -DARDUINO=10811 -DARDUINO_AVR_{options.Arduino.ToUpper()} -DARDUINO_ARCH_AVR -I{path}PrecompiledBinaries/arduino/avr/cores/arduino -I{path}PrecompiledBinaries/hardware/arduino/avr/variants/standard {path}PrecompiledBinaries/tmp/sketch/output.cpp -o /dev/null".Bash(); $"{path}PrecompiledBinaries/hardware/tools/avr/bin/avr-g++ {(options.Verbose ? "-v" : "")} -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu={options.Processor} -DF_CPU=16000000L -DARDUINO=10811 -DARDUINO_AVR_{options.Arduino.ToUpper()} -DARDUINO_ARCH_AVR -I{path}PrecompiledBinaries/arduino/avr/cores/arduino -I{path}PrecompiledBinaries/hardware/arduino/avr/variants/standard {path}PrecompiledBinaries/tmp/sketch/output.cpp -o {path}PrecompiledBinaries/tmp/Preproc/ctags_target_for_gcc_minus_e.cpp".Bash(); $"{path}PrecompiledBinaries/tools-builder/ctags/5.8-arduino11/ctags -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives {path}PrecompiledBinaries/tmp/Preproc/ctags_target_for_gcc_minus_e.cpp".Bash(); $"{path}PrecompiledBinaries/hardware/tools/avr/bin/avr-g++ {(options.Verbose ? "-v" : "")} -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu={options.Processor} -DF_CPU=16000000L -DARDUINO=10811 -DARDUINO_AVR_{options.Arduino.ToUpper()} -DARDUINO_ARCH_AVR -I{path}PrecompiledBinaries/arduino/avr/cores/arduino -I{path}PrecompiledBinaries/hardware/arduino/avr/variants/standard {path}PrecompiledBinaries/tmp/sketch/output.cpp -o {path}PrecompiledBinaries/tmp/sketch/output.cpp.o".Bash(); $"{path}PrecompiledBinaries/hardware/tools/avr/bin/avr-gcc {(options.Verbose ? "-v" : "")} -w -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections -mmcu={options.Processor} -o {path}PrecompiledBinaries/tmp/output.cpp.elf {path}PrecompiledBinaries/tmp/sketch/output.cpp.o {path}PrecompiledBinaries/randomAFile.a -L{path}PrecompiledBinaries/tmp -lm".Bash(); $"{path}PrecompiledBinaries/hardware/tools/avr/bin/avr-objcopy {(options.Verbose ? "-v" : "")} -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 {path}PrecompiledBinaries/tmp/output.cpp.elf {path}PrecompiledBinaries/tmp/output.cpp.eep".Bash(); $"{path}PrecompiledBinaries/hardware/tools/avr/bin/avr-objcopy {(options.Verbose ? "-v" : "")} -O ihex -R .eeprom {path}PrecompiledBinaries/tmp/output.cpp.elf {path}PrecompiledBinaries/tmp/output.cpp.hex".Bash(); if (!options.OutputFile || options.DryRun) { $"{path}PrecompiledBinaries/unix/avrdude {(options.Verbose ? "-v" : "")} -C{path}PrecompiledBinaries/etc/avrdude.conf -v -p{options.Processor} -carduino -P{options.Port} -b115200 -D -Uflash:w:{path}PrecompiledBinaries/tmp/output.cpp.hex:i".Bash(); } } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.WriteLine("We're on Windows!"); if (options.Port == "COM0") { Console.Error.WriteLine($"Error: No Port Provided. The compiler will try to find one available."); ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = "powershell"; psi.UseShellExecute = false; psi.RedirectStandardOutput = true; psi.Arguments = "[System.IO.Ports.SerialPort]::getportnames()"; Process p = Process.Start(psi); string[] devices = p.StandardOutput.ReadToEnd().Split("\n"); p.WaitForExit(); if (devices.Any(str => str.Contains("COM"))) { options.Port = devices.Last(str => str.Contains("COM")); } } path = path.Replace('/', '\\'); $"\"{path}PrecompiledBinaries\\win\\avr-g++\" {(options.Verbose ? "-v" : "")} -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu={options.Processor} -DF_CPU=16000000L -DARDUINO=10812 -DARDUINO_AVR_{options.Arduino.ToUpper()} -DARDUINO_ARCH_AVR \"-I{path}PrecompiledBinaries\\arduino\\avr\\cores\\arduino\" \"-I{path}PrecompiledBinaries\\arduino\\avr\\variants\\standard\" \"{path}PrecompiledBinaries\\tmp\\sketch\\output.cpp\" -o nul".Cmd(); $"\"{path}PrecompiledBinaries\\win\\avr-g++\" {(options.Verbose ? "-v" : "")} -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu={options.Processor} -DF_CPU=16000000L -DARDUINO=10812 -DARDUINO_AVR_{options.Arduino.ToUpper()} -DARDUINO_ARCH_AVR \"-I{path}PrecompiledBinaries\\arduino\\avr\\cores\\arduino\" \"-I{path}PrecompiledBinaries\\arduino\\avr\\variants\\standard\" \"{path}PrecompiledBinaries\\tmp\\sketch\\output.cpp\" -o \"{path}PrecompiledBinaries\\tmp\\Preproc\\ctags_target_for_gcc_minus_e.cpp\"".Cmd(); $"\"{path}PrecompiledBinaries\\win\\ctags.exe\" {(options.Verbose ? "-v" : "")} -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives \"{path}PrecompiledBinaries\\tmp\\Preproc\\ctags_target_for_gcc_minus_e.cpp\"".Cmd(); $"\"{path}PrecompiledBinaries\\win\\avr-g++.exe\" {(options.Verbose ? "-v" : "")} -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu={options.Processor} -DF_CPU=16000000L -DARDUINO=10812 -DARDUINO_AVR_{options.Arduino.ToUpper()} -DARDUINO_ARCH_AVR \"-I{path}PrecompiledBinaries\\arduino\\avr\\cores\\arduino\" \"-I{path}PrecompiledBinaries\\arduino\\avr\\variants\\standard\" \"{path}PrecompiledBinaries\\tmp\\sketch\\output.cpp\" -o \"{path}PrecompiledBinaries\\tmp\\sketch\\output.cpp.o\"".Cmd(); $"\"{path}PrecompiledBinaries\\win\\avr-gcc.exe\" {(options.Verbose ? "-v" : "")} -w -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections -mmcu={options.Processor} -o \"{path}PrecompiledBinaries\\tmp\\output.cpp.elf\" \"{path}PrecompiledBinaries\\tmp\\sketch\\output.cpp.o\" \"{path}PrecompiledBinaries\\randomAFile.a\" \"-L{path}PrecompiledBinaries\\tmp\" -lm".Cmd(); $"\"{path}PrecompiledBinaries\\win\\avr-objcopy.exe\" {(options.Verbose ? "-v" : "")} -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 \"{path}PrecompiledBinaries\\tmp\\output.cpp.elf\" \"{path}PrecompiledBinaries\\tmp\\output.cpp.eep\"".Cmd(); $"\"{path}PrecompiledBinaries\\win\\avr-objcopy.exe\" {(options.Verbose ? "-v" : "")} -O ihex -R .eeprom \"{path}PrecompiledBinaries\\tmp\\output.cpp.elf\" \"{path}PrecompiledBinaries\\tmp\\output.cpp.hex\"".Cmd(); if (!options.OutputFile || options.DryRun) { $"\"{path}PrecompiledBinaries\\win\\avrdude\" {(options.Verbose ? "-v" : "")} \"-C{path}PrecompiledBinaries\\etc\\avrdude.conf\" -v -p{options.Processor} -carduino -P{options.Port} -b115200 -D \"-Uflash:w:{path}PrecompiledBinaries\\tmp\\output.cpp.hex:i\"".Cmd(); } } else { verbosePrinter.Error("OS not supported! Stopping."); } if (options.DryRun) { try { File.Delete($"{path}PrecompiledBinaries/tmp/sketch/output.cpp"); return(0); } catch (FileNotFoundException e) { verbosePrinter.Error("Encountered an error in code generation. Stopping."); Console.Error.WriteLine(e.Message); return(23); } } //TODO further compilation //"C:\\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-g++" -c -g -Os -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10812 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-IC:\\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino" "-IC:\\Program Files (x86)\Arduino\hardware\arduino\avr\variants\standard" "C:\Users\bruger\Documents\aau\4semester\\P4\github\\P4-program\Tests\CodeGeneration.Tests\bin\Debug\netcoreapp3.1\Codegen_output.cpp" -o "C:\Users\bruger\Documents\aau\4semester\\P4\github\\P4-program\Tests\CodeGeneration.Tests\bin\Debug\netcoreapp3.1\Codegen_output.cpp.o" } timer.Stop(); verbosePrinter.Info($"\nCompilation took {timer.Elapsed.TotalSeconds} seconds."); return(0); }