main function

Future<void> main(
  1. List<String> arguments
)

Main entry point for the Balbismo compiler.

Orchestrates the complete compilation pipeline based on command line arguments. The compilation process includes:

  1. Parsing command line arguments to determine compilation mode and input file
  2. Lexical and syntax analysis using flex-bison parser
  3. AST loading and semantic analysis
  4. LLVM IR code generation
  5. Optional optimization, assembly generation, or binary compilation
  6. Direct execution if requested

Command line usage:

balbismo <mode> <file> [args]

Supported modes:

  • gen-ir: Generate LLVM IR code
  • opt-ir: Generate optimized LLVM IR
  • gen-asm: Generate assembly code
  • compile: Compile to binary
  • run: Generate IR and execute with lli

Parameters:

  • arguments: Command line arguments passed to the compiler

Throws:

  • Exception when compilation fails at any stage

Implementation

Future<void> main(List<String> arguments) async{
  var mode = CompileModes.IR;
  var fileInName = "";
  var extraArgs = <String>[];
  if (arguments.length == 1) {
      printUsage();
  } else if (arguments.length > 1) {
    mode  = switch (arguments[0]){
      "gen-ir" => CompileModes.IR,
      "gen-asm" => CompileModes.ASM,
      "compile" => CompileModes.BIN,
      "run" => CompileModes.RUN,
      "opt-ir" => CompileModes.OPT_IR,
      _ => invalidMode(),
    };
    fileInName = arguments[1];
    extraArgs = arguments.sublist(2);
  } else {
    print("No file specified");
    printUsage();
  }
  String outputPath = switch (mode) {
    CompileModes.IR => "build/main.ll",
    CompileModes.OPT_IR => "build/main.ll",
    CompileModes.ASM => "build/main.s",
    CompileModes.BIN => "build/main",
    CompileModes.RUN => ""

  };
  // check if -o is in extraArgs
  if (extraArgs.contains("-o")) {
    var index = extraArgs.indexOf("-o");
    outputPath = extraArgs[index + 1];
    extraArgs.removeAt(index);
    extraArgs.removeAt(index);
  }

  Node.ir += "declare i32 @printf(i8*, ...)\n";
  Node.ir += "declare i32 @scanf(i8*, ...)\n";
    //check if build directory exists
  if (!Directory("build").existsSync()) {
    Directory("build").createSync();
    //check if tmp directory exists
    if (!Directory("build/tmp").existsSync()) {
      Directory("build/tmp").createSync();
    }
  }
  //run bison parser binary named balbismo_parser using process pass file to stdin
  var parser = await Process.start("lex-parse/balbismo_parser", ["-o", "build/tmp/ast.out"]);
  var codeFile = File(fileInName);


  codeFile.openRead().pipe(parser.stdin);
  print("Parsing with flex-bison");

  String parserError = await parser.stderr.transform(utf8.decoder).join("\n");
  int _ = await parser.exitCode;
  if (parserError.isNotEmpty) {
    print("-------------Parser failed---------------");
    print(parserError);

    exit(1);
  } else {
    print("---------------Parser finished successfully----------------");
  }
  print("Reading AST file: build/tmp/ast.out");
  var ast = parseAstFile("build/tmp/ast.out");
  print("-----------------AST Loaded Successfully-----------------");
  print("Generating IR code on build/tmp/main.ll");
  ast.evaluate(SymbolTable());
  File("build/tmp/main.ll").writeAsStringSync(Node.ir);
  print("-----------------IR Generated Successfully-----------------");


  switch (mode) {
    case CompileModes.IR:
      print("Outputting IR code to $outputPath");
      File(outputPath).writeAsStringSync(Node.ir);
    case CompileModes.OPT_IR:
      //run opt on build/main.ll
      var opt = await Process.start("opt", ["-S", "build/tmp/main.ll", "-o", outputPath, ...extraArgs]);
      print("Optimizing IR code with opt and outputting to $outputPath");
      opt.stdout.forEach((e)=>print(e));
      String optError = await opt.stderr.transform(utf8.decoder).join("\n");
      int optExitcode = await opt.exitCode;
      if (optError.isNotEmpty || optExitcode != 0) {
        print("-------------Opt failed---------------");
        print(optError);
        exit(1);
      }
      print("---------------Opt finished successfully----------------");
    case CompileModes.ASM:
      //run llc on build/main.ll
      print("Generating assembly with llc and outputting to $outputPath");
      var llc = await Process.start("llc", ["-o", outputPath, "build/tmp/main.ll", ...extraArgs]);
      llc.stdout.forEach((e)=>print(e));
      String llcError = await llc.stderr.transform(utf8.decoder).join("\n");
      int llcExitcode = await llc.exitCode;
      if (llcError.isNotEmpty || llcExitcode != 0) {
        print("-------------llc failed---------------");
        print(llcError);
        exit(1);
      }
      print("---------------llc finished successfully----------------");

    case CompileModes.BIN:
      // run clang on build/main.s
      print("Compiling with clang and outputting to $outputPath");
      var clang = await Process.start("clang", ["-o", outputPath, "build/tmp/main.ll", ...extraArgs]);
      clang.stdout.forEach((e)=>print(e));
      String clangError = await clang.stderr.transform(utf8.decoder).join("\n");
      int clangExitcode = await clang.exitCode;
      if (clangExitcode != 0) {
        print("-------------clang failed---------------");
        print(clangError);
        exit(1);
      }
      print("---------------clang finished successfully----------------");
    case CompileModes.RUN:
      //run lli on build/main.ll
      print("Running with lli");
      print("----program output:\n");
      var lli = await Process.start("stdbuf", ["-oL","lli", "build/tmp/main.ll", ...extraArgs]);
      lli.stdout.transform(utf8.decoder).forEach((element) => print(element));
      stdin.pipe(lli.stdin);


      String lliError = await lli.stderr.transform(utf8.decoder).join("\n");
      int lliExitcode = await lli.exitCode;
      if (lliExitcode != 0) {

        print("-------------lli failed---------------");
        print(lliError);
        exit(1);
      }
      print("---------------lli finished successfully----------------");
    //close stdin
    default:
      break;
  }

  print("-----------------Finished Compiling Successfully-----------------");
  exit(0);

}