From 984f0c0d99a2cb5432801ff6cfa3f976e61139d4 Mon Sep 17 00:00:00 2001
From: Giordano Salvador <73959795+e3m3@users.noreply.github.com>
Date: Fri, 20 Sep 2024 01:49:13 -0400
Subject: [PATCH] Enable object file and executable output
* Added C-derived main code generation, using clang
* Use llc and clang for object files and executables
* Added cli shorthand flags for convenience
* Removed lit crate dependency and tests
* Refactored code and tests
* Version bump
---
.gitignore | 4 +-
Cargo.toml | 3 +-
README.md | 23 +-
src/command.rs | 91 +++++
src/exit_code.rs | 12 +-
src/irgen.rs | 15 +-
src/lex.rs | 16 +-
src/main.c.template | 41 +++
src/main.rs | 310 ++++++++++++------
src/maingen.rs | 20 +-
src/maingen_c.rs | 146 +++++++++
src/module.rs | 202 +++++++++++-
src/options.rs | 187 ++++++++++-
src/target.rs | 49 ++-
tests/lit-llvm/codegen_type.calc | 14 -
tests/lit-llvm/end_to_end1.calc | 9 -
tests/lit-llvm/end_to_end2.calc | 49 ---
tests/lit-llvm/lex_other.calc | 19 --
tests/lit-llvm/parse_eoi.calc | 21 --
tests/lit-llvm/parse_idents.calc | 20 --
tests/lit-llvm/parse_numbers.calc | 31 --
tests/lit-rust/lex_hello.calc | 14 -
tests/lit-rust/lex_number.calc | 9 -
tests/lit-rust/usage.calc | 4 -
tests/lit-rust/version.calc | 5 -
tests/lit-tests-rust.rs | 39 ---
tests/{lit-tests-llvm.rs => lit-tests.rs} | 2 +-
tests/lit-tests/codegen_type.calc | 28 ++
tests/lit-tests/codegen_type_c.calc | 26 ++
tests/lit-tests/end_to_end1.calc | 8 +
tests/lit-tests/end_to_end2.calc | 49 +++
tests/lit-tests/end_to_end3.calc | 8 +
tests/lit-tests/end_to_end4.calc | 49 +++
tests/lit-tests/end_to_end_c1.calc | 7 +
tests/lit-tests/end_to_end_c2.calc | 47 +++
.../{lit-llvm => lit-tests}/input_file1.calc | 7 +-
tests/lit-tests/input_file_c1.calc | 9 +
tests/{lit-llvm => lit-tests}/irgen1.calc | 6 +-
tests/{lit-llvm => lit-tests}/irgen2.calc | 6 +-
.../{lit-llvm => lit-tests}/lex_comment.calc | 6 +-
tests/{lit-llvm => lit-tests}/lex_drop.calc | 4 +-
tests/{lit-llvm => lit-tests}/lex_hello.calc | 8 +-
tests/{lit-llvm => lit-tests}/lex_idents.calc | 14 +-
tests/{lit-llvm => lit-tests}/lex_nodrop.calc | 4 +-
tests/{lit-llvm => lit-tests}/lex_number.calc | 4 +-
.../lex_number_enhanced.calc | 14 +-
tests/{lit-rust => lit-tests}/lex_other.calc | 2 +-
tests/{lit-llvm => lit-tests}/lex_stdin.calc | 2 +-
.../lex_trailing_whitespace.calc | 4 +-
.../{lit-llvm => lit-tests}/lex_withdecl.calc | 6 +-
tests/{lit-llvm => lit-tests}/lit.cfg | 10 +
tests/{lit-llvm => lit-tests}/maingen1.calc | 4 +-
tests/{lit-llvm => lit-tests}/maingen2.calc | 4 +-
tests/lit-tests/option_c_main.calc | 60 ++++
.../option_notarget.calc | 8 +-
tests/{lit-llvm => lit-tests}/option_opt.calc | 9 +-
tests/lit-tests/parse_eoi.calc | 21 ++
.../parse_expr_arith.calc | 12 +-
.../parse_expr_unary_minus.calc | 14 +-
.../parse_expr_withdecl.calc | 4 +-
tests/lit-tests/parse_idents.calc | 20 ++
tests/lit-tests/parse_numbers.calc | 31 ++
tests/{lit-llvm => lit-tests}/sem_decl.calc | 10 +-
tests/{lit-llvm => lit-tests}/usage.calc | 0
tests/{lit-llvm => lit-tests}/version.calc | 2 +-
65 files changed, 1394 insertions(+), 488 deletions(-)
create mode 100644 src/command.rs
create mode 100644 src/main.c.template
create mode 100644 src/maingen_c.rs
delete mode 100644 tests/lit-llvm/codegen_type.calc
delete mode 100644 tests/lit-llvm/end_to_end1.calc
delete mode 100644 tests/lit-llvm/end_to_end2.calc
delete mode 100644 tests/lit-llvm/lex_other.calc
delete mode 100644 tests/lit-llvm/parse_eoi.calc
delete mode 100644 tests/lit-llvm/parse_idents.calc
delete mode 100644 tests/lit-llvm/parse_numbers.calc
delete mode 100644 tests/lit-rust/lex_hello.calc
delete mode 100644 tests/lit-rust/lex_number.calc
delete mode 100644 tests/lit-rust/usage.calc
delete mode 100644 tests/lit-rust/version.calc
delete mode 100644 tests/lit-tests-rust.rs
rename tests/{lit-tests-llvm.rs => lit-tests.rs} (98%)
create mode 100644 tests/lit-tests/codegen_type.calc
create mode 100644 tests/lit-tests/codegen_type_c.calc
create mode 100644 tests/lit-tests/end_to_end1.calc
create mode 100644 tests/lit-tests/end_to_end2.calc
create mode 100644 tests/lit-tests/end_to_end3.calc
create mode 100644 tests/lit-tests/end_to_end4.calc
create mode 100644 tests/lit-tests/end_to_end_c1.calc
create mode 100644 tests/lit-tests/end_to_end_c2.calc
rename tests/{lit-llvm => lit-tests}/input_file1.calc (85%)
create mode 100644 tests/lit-tests/input_file_c1.calc
rename tests/{lit-llvm => lit-tests}/irgen1.calc (73%)
rename tests/{lit-llvm => lit-tests}/irgen2.calc (62%)
rename tests/{lit-llvm => lit-tests}/lex_comment.calc (88%)
rename tests/{lit-llvm => lit-tests}/lex_drop.calc (81%)
rename tests/{lit-llvm => lit-tests}/lex_hello.calc (57%)
rename tests/{lit-llvm => lit-tests}/lex_idents.calc (75%)
rename tests/{lit-llvm => lit-tests}/lex_nodrop.calc (81%)
rename tests/{lit-llvm => lit-tests}/lex_number.calc (79%)
rename tests/{lit-llvm => lit-tests}/lex_number_enhanced.calc (77%)
rename tests/{lit-rust => lit-tests}/lex_other.calc (90%)
rename tests/{lit-llvm => lit-tests}/lex_stdin.calc (82%)
rename tests/{lit-llvm => lit-tests}/lex_trailing_whitespace.calc (90%)
rename tests/{lit-llvm => lit-tests}/lex_withdecl.calc (91%)
rename tests/{lit-llvm => lit-tests}/lit.cfg (86%)
rename tests/{lit-llvm => lit-tests}/maingen1.calc (98%)
rename tests/{lit-llvm => lit-tests}/maingen2.calc (97%)
create mode 100644 tests/lit-tests/option_c_main.calc
rename tests/{lit-llvm => lit-tests}/option_notarget.calc (69%)
rename tests/{lit-llvm => lit-tests}/option_opt.calc (60%)
create mode 100644 tests/lit-tests/parse_eoi.calc
rename tests/{lit-llvm => lit-tests}/parse_expr_arith.calc (76%)
rename tests/{lit-llvm => lit-tests}/parse_expr_unary_minus.calc (76%)
rename tests/{lit-llvm => lit-tests}/parse_expr_withdecl.calc (89%)
create mode 100644 tests/lit-tests/parse_idents.calc
create mode 100644 tests/lit-tests/parse_numbers.calc
rename tests/{lit-llvm => lit-tests}/sem_decl.calc (56%)
rename tests/{lit-llvm => lit-tests}/usage.calc (100%)
rename tests/{lit-llvm => lit-tests}/version.calc (75%)
diff --git a/.gitignore b/.gitignore
index 209a7a4..2114cbd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,5 +15,5 @@ Cargo.lock
*.bak
-tests/lit-llvm/.lit_test_times.txt
-tests/lit-llvm/Output
+tests/lit-tests/.lit_test_times.txt
+tests/lit-tests/Output
diff --git a/Cargo.toml b/Cargo.toml
index 1725c03..8de7ebd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "calcc"
-version = "0.1.0"
+version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -9,7 +9,6 @@ edition = "2021"
llvm-sys = { version = "=181.1.1", features = ["force-dynamic"] }
[dev-dependencies]
-lit = "1.0.4"
[lints.clippy]
unused_unit = "allow"
diff --git a/README.md b/README.md
index 7241248..9857fce 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ Learning [Rust][1] [[1]] by implementing the calc langauge using the [llvm-sys][
Implements the calc language, inspired by the [C++][3] [[3]] implementation presented by Macke and Kwan in [[4]] and [[5]].
Accepted factors in the grammar have been extended for convenience (see `src/{lex,parse}.rs` and `tests/lit-llvm/`).
-The output of the compiler is LLVM IR or LLVM bytecode [[6]].
+The output of the compiler is LLVM IR, LLVM bitcode, an object file, or executable file [[6]].
## Language
@@ -95,7 +95,9 @@ Notes:
* llvm18 and llvm-sys (or llvm version matching llvm-sys)
-* python3-lit, FileCheck, clang (for testing)
+* clang-18 (for executables and '-C|--c-main' flags)
+
+* python3-lit, FileCheck (for testing)
* By default, `tests/lit-tests-llvm.rs` will search for the lit executable in the `$PYTHON_VENV_PATH/bin`
(if it exists) or the system's `/usr/bin`.
@@ -143,17 +145,26 @@ usage: calcc [OPTIONS]
INPUT '-' (i.e., Stdin) or a file path
OPTIONS:
--ast Print the AST after parsing
+-b|--bitcode Output LLVM bitcode (post-optimization) (.bc if used with -o)
+-c Output an object file (post-optimization) (.o if used with -o)
--drop Drop unknown tokens instead of failing
-e|--expr[=] Process expression E instead of INPUT file
-h|--help Print this list of command line options
--lex Exit after running the lexer
--S|--llvmir Exit after outputting LLVM IR (post-optimization) instead of byte code
---nomain Omit linking with main module (i.e., output kernel only)
---notarget Omit target specific configuration in LLVM IR/bytecode
--o[=] Output to LLVM IR (.ll) or bytecode (.bc) file F instead of Stdout
+--ir Exit after printing IR (pre-optimization)
+-S|--llvmir Output LLVM IR (post-optimization) (.ll if used with -o)
+-k|--no-main Omit linking with main module (i.e., output kernel only)
+ When this option is selected, an executable cannot be generated
+--notarget Omit target specific configuration in LLVM IR/bitcode
+-o[=] Output to file F instead of Stdout
+ If no known extension is used (.bc|.exe|.ll|.o) an executable is assumed
+ An executable requires llc and clang to be installed
-O<0|1|2|3> Set the optimization level (default: O2)
--parse Exit after running the parser
--sem Exit after running the semantics check
+-C|--c-main Link with a C-derived main module (src/main.c.template)
+ This option is required for generating object files and executables on MacOS
+ and requires clang to be installed
-v|--verbose Enable verbose output
--version Display the package version and license information
```
diff --git a/src/command.rs b/src/command.rs
new file mode 100644
index 0000000..c0dbc0a
--- /dev/null
+++ b/src/command.rs
@@ -0,0 +1,91 @@
+// Copyright 2024, Giordano Salvador
+// SPDX-License-Identifier: BSD-3-Clause
+
+use crate::exit_code;
+use exit_code::exit;
+use exit_code::ExitCode;
+
+use std::io::Write;
+use std::process;
+use std::process::Stdio;
+
+pub struct CommandResult {
+ pub success: bool,
+ pub stdout: Option,
+}
+
+impl CommandResult {
+ pub fn new(success: bool, stdout: Option) -> Self {
+ CommandResult{success, stdout}
+ }
+}
+
+pub struct Command {}
+
+impl Command {
+ pub fn run(bin: &str, args: &[&str]) -> CommandResult {
+ let output = match process::Command::new(bin).args(args.iter()).output() {
+ Ok(output) => output,
+ Err(msg) => {
+ eprintln!("Failed to run with {}: {}", bin, msg);
+ exit(ExitCode::CommandError);
+ },
+ };
+ let stderr: &[u8] = output.stderr.as_slice();
+ let stdout: &[u8] = output.stdout.as_slice();
+ if !stderr.is_empty() {
+ eprintln!("{} stderr:\n{}", bin, std::str::from_utf8(stderr).unwrap());
+ }
+ let stdout_opt = if !stdout.is_empty() {
+ Some(std::str::from_utf8(stdout).unwrap().to_string())
+ } else {
+ None
+ };
+ CommandResult::new(output.status.success(), stdout_opt)
+ }
+
+ pub fn run_with_input(bin: &str, args: &[&str], input: &str) -> CommandResult {
+ let mut proc = match process::Command::new(bin)
+ .args(args.iter())
+ .stdin(Stdio::piped())
+ .spawn() {
+ Ok(proc) => proc,
+ Err(msg) => {
+ eprintln!("Failed to spawn {} child process: {}", bin, msg);
+ exit(ExitCode::CommandError);
+ },
+ };
+ let proc_stdin = match proc.stdin.as_mut() {
+ Some(stdin) => stdin,
+ None => {
+ eprintln!("Failed to get {} process stdin handle", bin);
+ exit(ExitCode::CommandError);
+ },
+ };
+ match proc_stdin.write_all(input.as_bytes()) {
+ Ok(()) => (),
+ Err(msg) => {
+ eprintln!("Failed to write input to {} process stdin: {}", bin, msg);
+ exit(ExitCode::CommandError);
+ },
+ };
+ let output = match proc.wait_with_output() {
+ Ok(output) => output,
+ Err(msg) => {
+ eprintln!("Failed to run with {}: {}", bin, msg);
+ exit(ExitCode::CommandError);
+ },
+ };
+ let stderr: &[u8] = output.stderr.as_slice();
+ let stdout: &[u8] = output.stdout.as_slice();
+ if !stderr.is_empty() {
+ eprintln!("{} stderr:\n{}", bin, std::str::from_utf8(stderr).unwrap());
+ }
+ let stdout_opt = if !stdout.is_empty() {
+ Some(std::str::from_utf8(stdout).unwrap().to_string())
+ } else {
+ None
+ };
+ CommandResult::new(output.status.success(), stdout_opt)
+ }
+}
diff --git a/src/exit_code.rs b/src/exit_code.rs
index e718a77..88a0a69 100644
--- a/src/exit_code.rs
+++ b/src/exit_code.rs
@@ -13,11 +13,13 @@ pub enum ExitCode {
SemanticError = 4,
ModuleError = 5,
IRGenError = 6,
- _MainGenError = 7,
- VerifyError = 8,
- TargetError = 9,
- LinkError = 10,
- WriteError = 11,
+ MainGenError = 7,
+ MainGenCError = 8,
+ VerifyError = 9,
+ TargetError = 10,
+ LinkError = 11,
+ WriteError = 12,
+ CommandError = 13,
}
pub fn exit(code: ExitCode) -> ! {
diff --git a/src/irgen.rs b/src/irgen.rs
index 70c7dc9..60b42be 100644
--- a/src/irgen.rs
+++ b/src/irgen.rs
@@ -3,7 +3,18 @@
extern crate llvm_sys as llvm;
-use llvm::core::*;
+use llvm::core::LLVMAddFunction;
+use llvm::core::LLVMAppendBasicBlockInContext;
+use llvm::core::LLVMBuildLoad2;
+use llvm::core::LLVMBuildNSWAdd;
+use llvm::core::LLVMBuildNSWMul;
+use llvm::core::LLVMBuildNSWSub;
+use llvm::core::LLVMBuildRet;
+use llvm::core::LLVMBuildSDiv;
+use llvm::core::LLVMBuildStore;
+use llvm::core::LLVMFunctionType;
+use llvm::core::LLVMGetParam;
+use llvm::core::LLVMPositionBuilderAtEnd;
use llvm::prelude::LLVMBasicBlockRef;
use llvm::prelude::LLVMBool;
use llvm::prelude::LLVMTypeRef;
@@ -34,7 +45,7 @@ pub struct IRGen<'a, 'b> {
}
impl <'a, 'b> IRGen<'a, 'b> {
- pub fn new(bundle: &'a mut ModuleBundle<'b>) -> Self {
+ fn new(bundle: &'a mut ModuleBundle<'b>) -> Self {
IRGen{bundle}
}
diff --git a/src/lex.rs b/src/lex.rs
index c6c7aa2..68ce8ff 100644
--- a/src/lex.rs
+++ b/src/lex.rs
@@ -35,7 +35,7 @@ pub enum TokenKind {
}
pub fn token_kind_to_string(k: TokenKind) -> String {
- String::from(match k {
+ match k {
TokenKind::Comma => "Comma",
TokenKind::Comment => "Comment",
TokenKind::Colon => "Colon",
@@ -51,7 +51,7 @@ pub fn token_kind_to_string(k: TokenKind) -> String {
TokenKind::Star => "Star",
TokenKind::Unknown => "Unknown",
TokenKind::With => "With",
- })
+ }.to_string()
}
#[derive(Clone)]
@@ -273,17 +273,7 @@ impl <'a, T: Read> Lexer<'a, T> {
}
fn is_other(c: char) -> bool {
- match c {
- ',' => true,
- ':' => true,
- '-' => true,
- '(' => true,
- ')' => true,
- '+' => true,
- '/' => true,
- '*' => true,
- _ => false,
- }
+ matches!(c, ',' | ':' | '-' | '(' | ')' | '+' | '/' | '*')
}
fn is_whitespace(c: char) -> bool {
diff --git a/src/main.c.template b/src/main.c.template
new file mode 100644
index 0000000..21251f2
--- /dev/null
+++ b/src/main.c.template
@@ -0,0 +1,41 @@
+// Copyright 2024, Giordano Salvador
+// SPDX-License-Identifier: BSD-3-Clause
+
+/// Description: A C-stub used to generate the main module for linking during compile time.
+/// Values prefixed by `@@` are expected inputs from the compiler for text substitution.
+/// Inputs:
+/// * NUM_ARGS : a `usize` for the length of the parameters list
+/// * USAGE_ARGS : a comma separated list of args for the usage prompt
+/// : (e.g., , , ...
+/// * PARAM_TYPES_LIST : a comma separated list of types for the callee prototype
+/// * PARAM_DECLS_LIST : a sequence of statements assigning temporaries for the callee
+/// (e.g., const t_i64 p0 = (t_i64)atoll(argv[BASE + 0]); ... )
+/// * PARAMS_LIST : a comma separated list of uses of the temporaries for the callee
+/// (e.g., p0, p1, ... )
+
+#include
+#include
+
+#define BASE 1
+#define NUM_ARGS @@NUM_ARGS
+#define USAGE " @@USAGE_ARGS\n"
+
+typedef long long t_i64;
+extern t_i64 calcc_main(@@PARAM_TYPES_LIST);
+
+int main(int argc, char **argv) {
+ if (argc != BASE + NUM_ARGS) {
+ (void) fprintf(stderr, "Invalid number of args to main. Expected %d args\n", NUM_ARGS);
+ (void) fprintf(stderr, USAGE);
+ return 1;
+ }
+
+ /* Parameter declaration section: */
+ @@PARAM_DECLS_LIST
+
+ /* Function call section: */
+ const t_i64 result = calcc_main(@@PARAMS_LIST);
+ (void) printf("calcc_main result: %lld\n", result);
+
+ return 0;
+}
diff --git a/src/main.rs b/src/main.rs
index d1cdb00..99187d6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,19 +5,23 @@ const LICENSE: &str = "\
// SPDX-License-Identifier: BSD-3-Clause\n\
";
const PACKAGE: &str = "calcc";
-const VERSION: &str = "v0.1.0-dev";
+const VERSION: &str = "v0.2.0";
use std::env;
+use std::fmt;
use std::fs::File;
use std::io::Cursor;
use std::io::stdin;
-use std::io::Write;
+use std::path::Path;
+use std::process;
mod ast;
+mod command;
mod exit_code;
mod irgen;
mod lex;
mod maingen;
+mod maingen_c;
mod module;
mod parse;
mod options;
@@ -32,12 +36,17 @@ use irgen::IRGen;
use lex::Lexer;
use lex::Token;
use maingen::MainGen;
+use maingen_c::MainGenC;
use module::ModuleBundle;
-use options::OptLevel;
+use options::BodyType;
use options::CodeGenType;
+use options::HostOS;
+use options::OptLevel;
+use options::OutputType;
use options::RunOptions;
use parse::Parser;
use sem::Semantics;
+use target::Passes;
use target::PassBuilder;
use target::Target;
use target::TargetMachine;
@@ -47,17 +56,26 @@ fn help(code: ExitCode) -> ! {
"INPUT '-' (i.e., Stdin) or a file path",
"OPTIONS:",
"--ast Print the AST after parsing",
+ "-b|--bitcode Output LLVM bitcode (post-optimization) (.bc if used with -o)",
+ "-c Output an object file (post-optimization) (.o if used with -o)",
"--drop Drop unknown tokens instead of failing",
"-e|--expr[=] Process expression E instead of INPUT file",
"-h|--help Print this list of command line options",
"--lex Exit after running the lexer",
- "-S|--llvmir Exit after outputting LLVM IR (post-optimization) instead of byte code",
- "--nomain Omit linking with main module (i.e., output kernel only)",
- "--notarget Omit target specific configuration in LLVM IR/bytecode",
- "-o[=] Output to LLVM IR (.ll) or bytecode (.bc) file F instead of Stdout",
+ "--ir Exit after printing IR (pre-optimization)",
+ "-S|--llvmir Output LLVM IR (post-optimization) (.ll if used with -o)",
+ "-k|--no-main Omit linking with main module (i.e., output kernel only)",
+ " When this option is selected, an executable cannot be generated",
+ "--notarget Omit target specific configuration in LLVM IR/bitcode",
+ "-o[=] Output to file F instead of Stdout",
+ " If no known extension is used (.bc|.exe|.ll|.o) an executable is assumed",
+ " An executable requires llc and clang to be installed",
"-O<0|1|2|3> Set the optimization level (default: O2)",
"--parse Exit after running the parser",
"--sem Exit after running the semantics check",
+ "-C|--c-main Link with a C-derived main module (src/main.c.template)",
+ " This option is required for generating object files and executables on MacOS",
+ " and requires clang to be installed",
"-v|--verbose Enable verbose output",
"--version Display the package version and license information",
].join("\n"));
@@ -72,25 +90,98 @@ enum InputType<'a> {
File(&'a str),
}
-fn input_type_to_string(t: InputType) -> String {
- match t {
- InputType::Stdin => "Stdin".to_string(),
- InputType::Expr(e) => format!("Expression:{}", e),
- InputType::File(f) => format!("File:{}", f),
- InputType::None => "None".to_string(),
+impl <'a> fmt::Display for InputType<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = match self {
+ InputType::Stdin => "Stdin".to_string(),
+ InputType::Expr(e) => format!("Expression:{}", e),
+ InputType::File(f) => format!("File:{}", f),
+ InputType::None => "None".to_string(),
+ };
+ write!(f, "{}", s)
+ }
+}
+
+fn set_codegen_type(options: &mut RunOptions, codegen_type: CodeGenType) -> () {
+ if options.codegen_type == CodeGenType::Unset {
+ options.codegen_type = codegen_type;
+ } else {
+ eprintln!("Incompatible compiler flags for output type: {} and {}", options.codegen_type, codegen_type);
+ exit(ExitCode::ArgParseError);
+ }
+}
+
+fn set_body_type(options: &mut RunOptions, body_type: BodyType) -> () {
+ if options.body_type == BodyType::Unset {
+ options.body_type = body_type;
+ } else {
+ eprintln!("Incompatible compiler flags: '-k|--no-main' and '-C|--c-main'");
+ exit(ExitCode::ArgParseError);
}
}
#[derive(Clone,Copy)]
-enum OutputType<'a> {
- Stdout,
- File(&'a str),
+enum ExtType {
+ None,
+ BC,
+ Exe,
+ LL,
+ O,
}
-fn output_type_to_string(t: OutputType) -> String {
- match t {
- OutputType::Stdout => "Stdout".to_string(),
- OutputType::File(f) => format!("File:{}", f),
+fn get_extension_from_filename(name: &str) -> ExtType {
+ match Path::new(name).extension() {
+ None => ExtType::None,
+ Some(ext) => match ext.to_str().unwrap() {
+ "bc" => ExtType::BC,
+ "exe" => ExtType::Exe,
+ "ll" => ExtType::LL,
+ "o" => ExtType::O,
+ _ => ExtType::None,
+ },
+ }
+}
+
+/// Checks to ensure valid combination for BodyType, CodeGenType, and OutputType
+fn check_options_configuration(options: &RunOptions, output: &OutputType) -> () {
+ match *output {
+ OutputType::Stdout => (),
+ OutputType::File(f) => {
+ let t = options.codegen_type;
+ match get_extension_from_filename(f) {
+ ExtType::None => if t != CodeGenType::Executable {
+ eprintln!("Output name (no/unknown extension) should match codegen type: {} specified", t);
+ exit(ExitCode::ArgParseError);
+ },
+ ExtType::BC => if t != CodeGenType::Bitcode {
+ eprintln!("Output name ('.bc' extension) should match codegen type (-b|--bitcode)");
+ exit(ExitCode::ArgParseError);
+ },
+ ExtType::Exe => if t != CodeGenType::Executable {
+ eprintln!("Output name ('.exe' extension) should match codegen type: {} specified", t);
+ exit(ExitCode::ArgParseError);
+ },
+ ExtType::LL => if t != CodeGenType::Llvmir {
+ eprintln!("Output name ('.ll' extension) should match codegen type (-S|--llvmir)");
+ exit(ExitCode::ArgParseError);
+ },
+ ExtType::O => if t != CodeGenType::Object {
+ eprintln!("Output name ('.o' extension) should match codegen type (-c)");
+ exit(ExitCode::ArgParseError);
+ },
+ }
+ },
+ };
+
+ if options.body_type == BodyType::NoMain && options.codegen_type == CodeGenType::Executable {
+ eprintln!("Unsupported -k-|-no-main with executable output type");
+ exit(ExitCode::ArgParseError);
+ }
+
+ if options.host_os == HostOS::MacOS && options.body_type == BodyType::MainGen {
+ eprintln!("Linking the C standard library from a kernel+main module is not supported on MacOS");
+ eprintln!("Please use the C-derived main option (-C|--c-main)");
+ exit(ExitCode::ArgParseError);
}
}
@@ -108,14 +199,20 @@ fn parse_args<'a>(
arg = args.get(i).unwrap();
match arg.as_str() {
"--ast" => options.print_ast = true,
+ "-b" => set_codegen_type(options, CodeGenType::Bitcode),
+ "--bitcode" => set_codegen_type(options, CodeGenType::Bitcode),
+ "-c" => set_codegen_type(options, CodeGenType::Object),
+ "-C" => set_body_type(options, BodyType::MainGenC),
"--drop" => options.drop_token = true,
"-e" => *input = InputType::Expr(parse_arg_after(args, &mut i)),
"--expr" => *input = InputType::Expr(parse_arg_after(args, &mut i)),
"-h" => help(ExitCode::Ok),
"--help" => help(ExitCode::Ok),
+ "--ir" => options.ir_exit = true,
+ "-k" => set_body_type(options, BodyType::NoMain),
"--lex" => options.lex_exit = true,
- "--llvmir" => options.codegen_type = CodeGenType::Llvmir,
- "--nomain" => options.no_main = true,
+ "--llvmir" => set_codegen_type(options, CodeGenType::Llvmir),
+ "--no-main" => set_body_type(options, BodyType::NoMain),
"--notarget" => options.no_target = true,
"-o" => *output = OutputType::File(parse_arg_after(args, &mut i)),
"-O0" => options.opt_level = OptLevel::O0,
@@ -124,7 +221,8 @@ fn parse_args<'a>(
"-O3" => options.opt_level = OptLevel::O3,
"--parse" => options.parse_exit = true,
"--sem" => options.sem_exit = true,
- "-S" => options.codegen_type = CodeGenType::Llvmir,
+ "-S" => set_codegen_type(options, CodeGenType::Llvmir),
+ "--c-main" => set_body_type(options, BodyType::MainGenC),
"-v" => options.verbose = true,
"--verbose" => options.verbose = true,
"--version" => print_pkg_info(true),
@@ -133,37 +231,28 @@ fn parse_args<'a>(
i += 1;
}
+ if options.body_type == BodyType::Unset {
+ set_body_type(options, BodyType::MainGen);
+ }
+
+ if options.codegen_type == CodeGenType::Unset {
+ set_codegen_type(options, CodeGenType::Executable);
+ }
+
if *input == InputType::None {
eprintln!("No input file/name specified!");
help(ExitCode::ArgParseError);
} else if options.verbose {
- eprintln!("Processing input '{}'", input_type_to_string(*input));
+ eprintln!("Processing input '{}'", *input);
}
- match *output {
- OutputType::Stdout => (),
- OutputType::File(f) => {
- let len = f.len();
- if len < 3 {
- eprintln!("Malformed output name '{}'", f);
- exit(ExitCode::ArgParseError);
- }
- let ext: &str = &f[len-3..];
- if ext != ".bc" && ext != ".ll" {
- eprintln!("Output name '{}' should end in '.bc' or '.ll", f);
- exit(ExitCode::ArgParseError);
- } else if ext == ".bc" && options.codegen_type == CodeGenType::Llvmir {
- eprintln!("LLVM IR file type (with '-S' flag) should match output name ('.ll' extension)");
- exit(ExitCode::ArgParseError);
- } else if ext == ".ll" && options.codegen_type == CodeGenType::Bytecode {
- eprintln!("Bytecode file type (missing '-S' flag) should match output name ('.bc' extension)");
- exit(ExitCode::ArgParseError);
- }
- },
- };
+ if options.verbose {
+ eprintln!("Outputting to '{}'", *output);
+ }
+ check_options_configuration(options, output);
if options.verbose {
- eprintln!("Outputting to '{}'", output_type_to_string(*output));
+ eprintln!("{}", options);
}
}
@@ -206,7 +295,7 @@ fn parse_arg_complex<'a>(
}
}
} else if *input != InputType::None {
- eprintln!("Found more than one input ('{}' and '{}')", input_type_to_string(*input), arg);
+ eprintln!("Found more than one input ('{}' and '{}')", *input, arg);
help(ExitCode::ArgParseError);
} else if arg.len() == 1 && lead_char == '-' {
*input = InputType::Stdin;
@@ -220,47 +309,6 @@ fn print_pkg_info(should_exit: bool) {
if should_exit { exit(ExitCode::Ok); }
}
-fn write_module(bundle: &mut ModuleBundle, codegen_type: CodeGenType, output: &OutputType) -> bool {
- match *output {
- OutputType::Stdout => {
- match codegen_type {
- CodeGenType::Llvmir => {
- let string: String = bundle.to_string();
- println!("{}", string);
- },
- CodeGenType::Bytecode => {
- eprintln!("Unimplemented: writing bytecode to Stdout");
- return false;
- },
- }
- },
- OutputType::File(f) => {
- match codegen_type {
- CodeGenType::Llvmir => {
- let string: String = bundle.to_string();
- let mut file = match File::create(f) {
- Ok(file) => file,
- Err(msg) => {
- eprintln!("Failed to open output file '{}': {}", f, msg);
- return false;
- }
- };
- match file.write_all(string.as_bytes()) {
- Ok(()) => (),
- Err(msg) => {
- eprintln!("Failed to write to output file '{}': {}", f, msg);
- return false;
- }
- }
- },
- CodeGenType::Bytecode => bundle.write_bitcode_to_file(f),
- }
- },
- }
-
- true
-}
-
fn main() -> ! {
let args: Vec = env::args().collect();
let mut input: InputType = InputType::None;
@@ -309,40 +357,88 @@ fn main() -> ! {
}
let module_name_main = String::from("main");
- let mut module_main = if options.no_main {
- module_irgen
- } else {
- let mut module_main = ModuleBundle::new(&module_name_main, options.verbose);
+ let mut module_main = match options.body_type {
+ BodyType::NoMain => module_irgen,
+ BodyType::MainGen => {
+ let mut module_main = ModuleBundle::new(&module_name_main, options.verbose);
- // NOTE: Fixing the verifier errors by reconstructing the callee signature using the correct main module
- // context causes the link step to omit the body of the callee (emitting only a declaration).
- //let f_sig = module_main.get_f_sig_from_context(&module_irgen.f_sig.clone().unwrap());
- let f_sig = &module_irgen.f_sig.clone().unwrap();
+ // NOTE: Fixing the verifier errors by reconstructing the callee signature using
+ // the correct main module context causes the link step to omit the body of the callee
+ // (emitting only a declaration).
+ //let f_sig = module_main.get_f_sig_from_context(&module_irgen.f_sig.clone().unwrap());
+ let f_sig = &module_irgen.f_sig.clone().unwrap();
- let maingen_status: bool = MainGen::gen(&mut module_main, f_sig);
- assert!(maingen_status);
+ let maingen_status: bool = MainGen::gen(&mut module_main, f_sig);
+ if !maingen_status {
+ eprintln!("Failed to generate MainGen module");
+ exit(ExitCode::MainGenError);
+ }
- // NOTE: Linking modules causes verifier errors for unmatched contexts of LLVMTypeRef. Skip verification.
- let link_status: bool = module_main.link_into(&mut module_irgen);
- if !link_status {
- eprintln!("IRGen and MainGen modules failed to link");
- exit(ExitCode::LinkError);
- }
- module_main
+ // NOTE: Linking modules causes verifier errors for unmatched contexts of LLVMTypeRef.
+ // Skip verification.
+ let link_status: bool = module_main.link_into(&mut module_irgen);
+ if !link_status {
+ eprintln!("IRGen and MainGen modules failed to link");
+ exit(ExitCode::LinkError);
+ }
+
+ module_main
+ },
+ BodyType::MainGenC => {
+ // Generating the main.c object need to happen until after the target/optimization step,
+ // but to appease the borrow checker, we do it here.
+ let f_sig = &module_irgen.f_sig.clone().unwrap();
+ let stem = format!("{}pid{}",
+ match input {
+ InputType::File(f) => Path::new(f).file_stem().unwrap().to_str().unwrap(),
+ _ => "",
+ },
+ process::id()
+ );
+ let main_obj_path = match MainGenC::gen(f_sig, &stem, options.verbose) {
+ Some(path) => path,
+ None => {
+ eprintln!("Failed to generate MainGenC object file");
+ exit(ExitCode::MainGenCError);
+ },
+ };
+
+ module_irgen.push_object(main_obj_path);
+ module_irgen
+ },
+ BodyType::Unset => {
+ eprintln!("Invalid body type");
+ exit(ExitCode::ModuleError);
+ },
};
+ match input {
+ InputType::File(f) => module_main.set_sourcefile_name(f),
+ _ => module_main.set_sourcefile_name("-"),
+ };
+
+ if options.ir_exit {
+ eprintln!("{}", module_main);
+ exit(ExitCode::Ok);
+ }
+
let mut target = Target::new();
if options.verbose {
eprintln!("Detected target triple '{}'", target.get_string());
}
let mut machine = TargetMachine::new(&mut target, options.opt_level);
let mut pass_builder = PassBuilder::new();
- let opt_result = pass_builder.run(&mut module_main, &mut machine, options.opt_level, options.no_target);
+ let opt_result = pass_builder.run(
+ &mut module_main,
+ &mut machine,
+ Passes::Default(options.opt_level),
+ options.no_target
+ );
if !opt_result {
exit(ExitCode::TargetError);
}
- let write_result: bool = write_module(&mut module_main, options.codegen_type, &output);
+ let write_result = module_main.write_module(&options, &output);
if !write_result {
eprintln!("Failed to write module to output");
exit(ExitCode::WriteError);
diff --git a/src/maingen.rs b/src/maingen.rs
index 36ea934..9aa094a 100644
--- a/src/maingen.rs
+++ b/src/maingen.rs
@@ -5,11 +5,25 @@
/// program function generated for the input program.
/// This approach is used to avoid interfacing with a stub program written in C, directly calling
/// the dependent library functions.
-/// Omit this code generation by passing '--nomain' to calcc.
+/// Omit this code generation by passing '--no-main' to calcc.
extern crate llvm_sys as llvm;
-use llvm::core::*;
+use llvm::core::LLVMAddFunction;
+use llvm::core::LLVMAddGlobal;
+use llvm::core::LLVMAppendBasicBlockInContext;
+use llvm::core::LLVMBuildBr;
+use llvm::core::LLVMBuildCall2;
+use llvm::core::LLVMBuildCondBr;
+use llvm::core::LLVMBuildGEP2;
+use llvm::core::LLVMBuildICmp;
+use llvm::core::LLVMBuildLoad2;
+use llvm::core::LLVMBuildRet;
+use llvm::core::LLVMBuildStore;
+use llvm::core::LLVMFunctionType;
+use llvm::core::LLVMGetParam;
+use llvm::core::LLVMPositionBuilderAtEnd;
+use llvm::core::LLVMSetAlignment;
use llvm::prelude::LLVMBasicBlockRef;
use llvm::prelude::LLVMBool;
use llvm::prelude::LLVMTypeRef;
@@ -46,7 +60,7 @@ pub struct MainGen<'a, 'b> {
}
impl <'a, 'b> MainGen<'a, 'b> {
- pub fn new(bundle: &'a mut ModuleBundle<'b>) -> Self {
+ fn new(bundle: &'a mut ModuleBundle<'b>) -> Self {
MainGen{bundle}
}
diff --git a/src/maingen_c.rs b/src/maingen_c.rs
new file mode 100644
index 0000000..82524b3
--- /dev/null
+++ b/src/maingen_c.rs
@@ -0,0 +1,146 @@
+// Copyright 2024, Giordano Salvador
+// SPDX-License-Identifier: BSD-3-Clause
+
+/// Generate LLVM IR for a main function, using the C standard library, from `main.c.template`.
+/// Enable this using `--c-main`/`-C`.
+
+extern crate llvm_sys as llvm;
+use llvm::prelude::LLVMTypeRef;
+
+use std::env;
+use std::path::Path;
+use std::str;
+
+use crate::command;
+use crate::module;
+use crate::exit_code;
+
+use command::Command;
+use exit_code::exit;
+use exit_code::ExitCode;
+use module::FunctionSignature;
+use module::ModuleBundle;
+
+pub struct MainGenC {}
+
+const INPUT_NUM_ARGS : &str = "@@NUM_ARGS";
+const INPUT_USAGE_ARGS : &str = "@@USAGE_ARGS";
+const INPUT_PARAM_TYPES_LIST: &str = "@@PARAM_TYPES_LIST";
+const INPUT_PARAM_DECLS_LIST: &str = "@@PARAM_DECLS_LIST";
+const INPUT_PARAMS_LIST : &str = "@@PARAMS_LIST";
+const MAIN_C_TEMPLATE : &str = include_str!("main.c.template");
+
+const CLANG_AGS: [&str; 9] = [
+ "-x", "c", "-", "-Wall", "-Werror", "-std=c99", "-fPIC", "-c", "-o"
+];
+
+impl MainGenC {
+ /// Returns a path (option) to the object file generated from `main.c.tempalte`.
+ /// This object file will be linked to the object file generated from the LLVM during the
+ /// IRGen stage.
+ pub fn gen(callee_sig: &FunctionSignature, stem: &str, verbose: bool) -> Option {
+ let body = Self::substitute_all_inputs_in_body(MAIN_C_TEMPLATE, callee_sig, verbose);
+ let mut clang_args: Vec<&str> = Vec::from(CLANG_AGS);
+ let dir = env::temp_dir();
+ let main_obj_path = Self::get_main_obj_path(&dir, stem);
+ if verbose {
+ eprintln!("Path to main object file: {}", main_obj_path);
+ }
+ clang_args.push(main_obj_path.as_str());
+ let clang_result = Command::run_with_input("clang", &clang_args, &body);
+ if clang_result.stdout.is_some() {
+ println!("{}", clang_result.stdout.unwrap());
+ }
+ if clang_result.success {
+ Some(main_obj_path)
+ } else {
+ None
+ }
+ }
+
+ fn get_main_obj_path(dir: &Path, stem: &str) -> String {
+ match dir.join(format!("main.{}.o", stem)).as_path().to_str() {
+ Some(path) => path,
+ None => {
+ eprintln!("Failed to create path for main.o");
+ exit(ExitCode::MainGenCError);
+ },
+ }.to_string()
+ }
+
+ fn substitute_all_inputs_in_body(body: &str, callee_sig: &FunctionSignature, verbose: bool) -> String {
+ let num_args = format!("{}", callee_sig.params.len());
+ let usage_string = Self::get_usage_args_string(&callee_sig.params);
+ let param_decls = Self::collect_callee_param_decls_string(&callee_sig.params, 1);
+ let param_types_list = Self::get_callee_param_types_list_string(&callee_sig.params);
+ let params_list = Self::get_callee_params_list_string(&callee_sig.params);
+ let body_with_num_args = Self::substitute_param_in_string(body, INPUT_NUM_ARGS, &num_args);
+ let body_with_usage_args = Self::substitute_param_in_string(
+ &body_with_num_args, INPUT_USAGE_ARGS, &usage_string
+ );
+ let body_with_param_decls = Self::substitute_param_in_string(
+ &body_with_usage_args, INPUT_PARAM_DECLS_LIST, ¶m_decls
+ );
+ let body_with_param_types_list = Self::substitute_param_in_string(
+ &body_with_param_decls, INPUT_PARAM_TYPES_LIST, ¶m_types_list
+ );
+ let body_with_params_list = Self::substitute_param_in_string(
+ &body_with_param_types_list, INPUT_PARAMS_LIST, ¶ms_list
+ );
+ if verbose {
+ eprintln!("Body of 'main.c' after input substitution:\n{}", body_with_params_list);
+ }
+ body_with_params_list
+ }
+
+ fn get_usage_args_string(params: &[LLVMTypeRef]) -> String {
+ let mut args_string = String::new();
+ for i in 0..params.len() {
+ let sep = if i == 0 { "" } else { ", " };
+ args_string = args_string + sep + format!("", i).as_str();
+ }
+ args_string
+ }
+
+ fn collect_callee_param_decls_string(params: &[LLVMTypeRef], indent_depth: usize) -> String {
+ let mut decls: Vec = Vec::new();
+ for (i, t) in params.iter().enumerate() {
+ decls.push(Self::get_callee_param_decl_string(i, *t));
+ }
+ let mut join_str = String::from("\n");
+ for _ in 0..indent_depth {
+ join_str += " ";
+ }
+ decls.join(join_str.as_str())
+ }
+
+ /// Generates an assignment to a constant integer from a call to atoll
+ fn get_callee_param_decl_string(idx: usize, t: LLVMTypeRef) -> String {
+ let t_str = format!("t_{}", ModuleBundle::type_name_from(t));
+ format!("const {} p{} = ({})atoll(argv[BASE + {}]);", t_str, idx, t_str, idx)
+ }
+
+ fn get_callee_param_types_list_string(params: &[LLVMTypeRef]) -> String {
+ let mut params_string = String::new();
+ for (i, t) in params.iter().enumerate() {
+ let sep = if i == 0 { "" } else { ", " };
+ let t_name = format!("t_{}", ModuleBundle::type_name_from(*t));
+ params_string = params_string + sep + &t_name;
+ }
+ params_string
+ }
+
+ fn get_callee_params_list_string(params: &[LLVMTypeRef]) -> String {
+ let n = params.len();
+ let mut params_string = String::new();
+ for i in 0..n {
+ let sep = if i == 0 { "" } else { ", " };
+ params_string = params_string + sep + format!("p{}", i).as_str();
+ }
+ params_string
+ }
+
+ fn substitute_param_in_string(body: &str, old: &str, new: &str) -> String {
+ str::replace(body, old, new).to_string()
+ }
+}
diff --git a/src/module.rs b/src/module.rs
index 7bac11c..6e0a2d7 100644
--- a/src/module.rs
+++ b/src/module.rs
@@ -3,10 +3,28 @@
extern crate llvm_sys as llvm;
-use llvm::core::*;
use llvm::analysis::LLVMVerifierFailureAction;
use llvm::analysis::LLVMVerifyModule;
use llvm::bit_writer::LLVMWriteBitcodeToFile;
+use llvm::core::LLVMAddFunction;
+use llvm::core::LLVMBuildAlloca;
+use llvm::core::LLVMBuildGlobalString;
+use llvm::core::LLVMConstInt;
+use llvm::core::LLVMContextCreate;
+use llvm::core::LLVMContextDispose;
+use llvm::core::LLVMCreateBuilderInContext;
+use llvm::core::LLVMDisposeBuilder;
+use llvm::core::LLVMDisposeMessage;
+use llvm::core::LLVMDisposeModule;
+use llvm::core::LLVMFunctionType;
+use llvm::core::LLVMGetIntTypeWidth;
+use llvm::core::LLVMGetTypeKind;
+use llvm::core::LLVMInt32TypeInContext;
+use llvm::core::LLVMInt64TypeInContext;
+use llvm::core::LLVMModuleCreateWithNameInContext;
+use llvm::core::LLVMPointerTypeInContext;
+use llvm::core::LLVMPrintModuleToString;
+use llvm::core::LLVMSetSourceFileName;
use llvm::linker::LLVMLinkModules2;
use llvm::prelude::LLVMBool;
use llvm::prelude::LLVMBuilderRef;
@@ -14,8 +32,10 @@ use llvm::prelude::LLVMContextRef;
use llvm::prelude::LLVMModuleRef;
use llvm::prelude::LLVMTypeRef;
use llvm::prelude::LLVMValueRef;
+use llvm::LLVMTypeKind;
use std::collections::HashMap;
+use std::env;
use std::ffi::c_char;
use std::ffi::c_int;
use std::ffi::c_uint;
@@ -23,12 +43,21 @@ use std::ffi::c_ulonglong;
use std::ffi::CStr;
use std::fmt;
use std::fmt::Display;
+use std::fs::File;
+use std::io::Write;
+use std::path::Path;
use std::ptr;
+use crate::command;
use crate::exit_code;
+use crate::options;
+use command::Command;
use exit_code::exit;
use exit_code::ExitCode;
+use options::CodeGenType;
+use options::OutputType;
+use options::RunOptions;
#[derive(Clone)]
pub struct FunctionSignature {
@@ -45,35 +74,37 @@ impl FunctionSignature {
pub struct ModuleBundle<'a> {
pub builder: LLVMBuilderRef,
pub context: LLVMContextRef,
+ pub f: Option,
+ pub f_sig: Option,
pub module: LLVMModuleRef,
pub name: &'a String,
+ pub objects: Vec,
pub scope: Scope,
pub t_i32: LLVMTypeRef,
pub t_i64: LLVMTypeRef,
pub t_opaque: LLVMTypeRef,
- pub f: Option,
- pub f_sig: Option,
pub verbose: bool,
}
impl <'a> ModuleBundle<'a> {
pub fn new(name: &'a String, verbose: bool) -> Self {
let bundle = unsafe {
- let c = LLVMContextCreate();
+ let context = LLVMContextCreate();
let n = Self::value_name(name.as_str());
- let m = LLVMModuleCreateWithNameInContext(n.as_ptr() as *const c_char, c);
- let b = LLVMCreateBuilderInContext(c);
+ let module = LLVMModuleCreateWithNameInContext(n.as_ptr() as *const c_char, context);
+ let builder = LLVMCreateBuilderInContext(context);
ModuleBundle{
- builder: b,
- context: c,
- module: m,
- name,
- scope: Scope::new(),
- t_i32: LLVMInt32TypeInContext(c),
- t_i64: LLVMInt64TypeInContext(c),
- t_opaque: LLVMPointerTypeInContext(c, 0 as c_uint),
+ builder,
+ context,
f: None,
f_sig: None,
+ module,
+ name,
+ objects: Vec::new(),
+ scope: Scope::new(),
+ t_i32: LLVMInt32TypeInContext(context),
+ t_i64: LLVMInt64TypeInContext(context),
+ t_opaque: LLVMPointerTypeInContext(context, 0 as c_uint),
verbose,
}
};
@@ -167,13 +198,153 @@ impl <'a> ModuleBundle<'a> {
}
pub fn write_bitcode_to_file(&mut self, f: &str) -> () {
- let name = String::from(f) + "\0";
+ let name = format!("{}\0", f);
let result: c_int = unsafe { LLVMWriteBitcodeToFile(self.module, name.as_ptr() as *const c_char) };
if result != 0 as c_int {
eprintln!("Failed to write module to file '{}'", name);
exit(ExitCode::WriteError);
}
}
+
+ pub fn set_sourcefile_name(&mut self, f: &str) -> () {
+ unsafe {
+ LLVMSetSourceFileName(self.module, f.as_ptr() as *const c_char, f.len());
+ }
+ }
+
+ pub fn get_int_width(t: LLVMTypeRef) -> usize {
+ let kind: LLVMTypeKind = unsafe { LLVMGetTypeKind(t) };
+ if kind != LLVMTypeKind::LLVMIntegerTypeKind {
+ eprintln!("Unexpected non-integer type");
+ exit(ExitCode::ModuleError);
+ };
+ (unsafe { LLVMGetIntTypeWidth(t) }) as usize
+ }
+
+ pub fn type_name_from(t: LLVMTypeRef) -> String {
+ let kind: LLVMTypeKind = unsafe { LLVMGetTypeKind(t) };
+ match kind {
+ LLVMTypeKind::LLVMPointerTypeKind => "ptr",
+ LLVMTypeKind::LLVMIntegerTypeKind => {
+ match Self::get_int_width(t) {
+ 32 => "i32",
+ 64 => "i64",
+ _ => {
+ eprintln!("Unsupported integer type width");
+ exit(ExitCode::ModuleError);
+ },
+ }
+ }
+ _ => {
+ eprintln!("Unsupported type kind");
+ exit(ExitCode::ModuleError);
+ }
+ }.to_string()
+ }
+
+ /// Objects required for linking the final object file/executable should be pushed before
+ /// output
+ pub fn push_object(&mut self, obj_path: String) -> () {
+ let path = Path::new(&obj_path);
+ if !path.is_file() || ".o" == path.extension().unwrap() {
+ eprintln!("Expected object file '{}'", obj_path);
+ exit(ExitCode::ModuleError);
+ }
+ self.objects.push(obj_path);
+ }
+
+ pub fn write_module(&mut self, options: &RunOptions, output: &OutputType) -> bool {
+ match *output {
+ OutputType::Stdout => {
+ match options.codegen_type {
+ CodeGenType::Llvmir => println!("{}", self),
+ _ => {
+ eprintln!("Unimplemented: writing {} to Stdout", options.codegen_type);
+ return false;
+ },
+ }
+ },
+ OutputType::File(f) => {
+ match options.codegen_type {
+ CodeGenType::Llvmir => {
+ let string: String = self.to_string();
+ let mut file = match File::create(f) {
+ Ok(file) => file,
+ Err(msg) => {
+ eprintln!("Failed to open output file '{}': {}", f, msg);
+ return false;
+ }
+ };
+ match file.write_all(string.as_bytes()) {
+ Ok(()) => (),
+ Err(msg) => {
+ eprintln!("Failed to write to output file '{}': {}", f, msg);
+ return false;
+ }
+ }
+ },
+ CodeGenType::Bitcode => self.write_bitcode_to_file(f),
+ CodeGenType::Object => {
+ let f_path = Path::new(f);
+ let temp_dir = env::temp_dir();
+ let f_stem = f_path.file_stem().unwrap().to_str().unwrap();
+ let f_bc = temp_dir.join(format!("{}.bc", f_stem));
+ let f_bc_str = f_bc.to_str().unwrap();
+ self.write_bitcode_to_file(f_bc_str);
+ self.object_file_from_bitcode(f, f_bc_str);
+ },
+ CodeGenType::Executable => {
+ let f_path = Path::new(f);
+ let temp_dir = env::temp_dir();
+ let f_stem = f_path.file_stem().unwrap().to_str().unwrap();
+ let f_bc = temp_dir.join(format!("{}.bc", f_stem));
+ let f_bc_str = f_bc.to_str().unwrap();
+ self.write_bitcode_to_file(f_bc_str);
+ let f_obj = temp_dir.join(format!("{}.obj", f_stem));
+ let f_obj_str = f_obj.to_str().unwrap();
+ self.object_file_from_bitcode(f_obj_str, f_bc_str);
+ self.executable_file_from_object(f, f_obj_str);
+ },
+ CodeGenType::Unset => {
+ eprintln!("Cannot write module with unset codegen type");
+ exit(ExitCode::WriteError);
+ },
+ }
+ },
+ }
+ true
+ }
+
+ fn object_file_from_bitcode(&self, f_obj: &str, f_bc: &str) -> () {
+ let arg_output = format!("-o={}", f_obj);
+ let args: Vec<&str> = vec![
+ "-relocation-model=pic",
+ "-filetype=obj",
+ arg_output.as_str(),
+ f_bc,
+ ];
+ let result_llc = Command::run("llc", &args);
+ if !result_llc.success {
+ eprintln!("Failed to write object file '{}' from bitcode file '{}'", f_obj, f_bc);
+ exit(ExitCode::WriteError);
+ } else if result_llc.stdout.is_some() {
+ println!("{}", result_llc.stdout.unwrap());
+ }
+ }
+
+ fn executable_file_from_object(&self, f_bin: &str, f_obj: &str) -> () {
+ let mut args: Vec<&str> = vec!["-o", f_bin, f_obj];
+ for object in self.objects.iter() {
+ args.push(object.as_str());
+ }
+ let result_clang = Command::run("clang", &args);
+ if !result_clang.success {
+ eprintln!("Failed to write executable file '{}' from object file '{}'", f_bin, f_obj);
+ exit(ExitCode::WriteError);
+ } else if result_clang.stdout.is_some() {
+ println!("{}", result_clang.stdout.unwrap());
+ }
+ }
}
impl <'a> Display for ModuleBundle<'a> {
@@ -192,7 +363,6 @@ impl <'a> Display for ModuleBundle<'a> {
}
}
-
impl <'a> Drop for ModuleBundle<'a> {
fn drop(&mut self) -> () {
self.scope.clear();
diff --git a/src/options.rs b/src/options.rs
index 514ae58..999c236 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -1,11 +1,16 @@
// Copyright 2024, Giordano Salvador
// SPDX-License-Identifier: BSD-3-Clause
+use std::fmt;
+
pub struct RunOptions {
+ pub body_type: BodyType,
pub codegen_type: CodeGenType,
pub drop_token: bool,
+ pub host_arch: HostArch,
+ pub host_os: HostOS,
+ pub ir_exit: bool,
pub lex_exit: bool,
- pub no_main: bool,
pub no_target: bool,
pub opt_level: OptLevel,
pub parse_exit: bool,
@@ -17,10 +22,13 @@ pub struct RunOptions {
impl RunOptions {
pub fn new() -> Self {
RunOptions{
- codegen_type: CodeGenType::Bytecode,
+ body_type: BodyType::Unset,
+ codegen_type: CodeGenType::Unset,
drop_token: false,
+ host_arch: get_host_arch(),
+ host_os: get_host_os(),
+ ir_exit: false,
lex_exit: false,
- no_main: false,
no_target: false,
opt_level: OptLevel::O2,
parse_exit: false,
@@ -31,27 +39,176 @@ impl RunOptions {
}
}
-#[repr(u8)]
+impl fmt::Display for RunOptions {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s_vec = vec![
+ "RunOptions:".to_string(),
+ format!("body_type: {}", self.body_type),
+ format!("codegen_type: {}", self.codegen_type),
+ format!("drop_token: {}", self.drop_token),
+ format!("host_arch: {}", self.host_arch),
+ format!("host_os: {}", self.host_os),
+ format!("ir_exit: {}", self.ir_exit),
+ format!("lex_exit: {}", self.lex_exit),
+ format!("no_target: {}", self.no_target),
+ format!("opt_level: {}", self.opt_level),
+ format!("parse_exit: {}", self.parse_exit),
+ format!("print_ast: {}", self.print_ast),
+ format!("sem_exit: {}", self.sem_exit),
+ format!("verbose: {}", self.verbose),
+ ];
+ write!(f, "{}", s_vec.join("\n "))
+ }
+}
+
#[derive(Clone,Copy)]
+pub enum OutputType<'a> {
+ Stdout,
+ File(&'a str),
+}
+
+impl <'a> fmt::Display for OutputType<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = match self {
+ OutputType::Stdout => "Stdout".to_string(),
+ OutputType::File(f) => format!("File:{}", f),
+ };
+ write!(f, "{}", s)
+ }
+}
+
+#[repr(u8)]
+#[derive(Clone,Copy,Default)]
pub enum OptLevel {
O0 = 0,
O1 = 1,
- O2 = 2, /// LLVM default opt level
+ #[default]
+ O2 = 2, /// LLVM default opt level
O3 = 3,
}
-pub fn opt_level_to_str(opt_level: OptLevel) -> String {
- String::from(match opt_level {
- OptLevel::O0 => "O0",
- OptLevel::O1 => "O1",
- OptLevel::O2 => "O2",
- OptLevel::O3 => "O3",
- })
+impl fmt::Display for OptLevel {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = match self {
+ OptLevel::O0 => "OptLevel_O0",
+ OptLevel::O1 => "OptLevel_O1",
+ OptLevel::O2 => "OptLevel_O2",
+ OptLevel::O3 => "OptLevel_O3",
+ };
+ write!(f, "{}", s)
+ }
+}
+
+#[repr(u8)]
+#[derive(Clone,Copy,Default,PartialEq)]
+pub enum BodyType {
+ #[default]
+ Unset = 0,
+ MainGen = 1,
+ MainGenC = 2,
+ NoMain = 3,
+}
+
+impl fmt::Display for BodyType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = match self {
+ BodyType::Unset => "BodyType_Unset",
+ BodyType::MainGen => "BodyType_MainGen",
+ BodyType::MainGenC => "BodyType_MainGenC",
+ BodyType::NoMain => "BodyType_NoMain",
+ };
+ write!(f, "{}", s)
+ }
}
#[repr(u8)]
-#[derive(Clone,Copy,PartialEq)]
+#[derive(Clone,Copy,Default,PartialEq)]
pub enum CodeGenType {
- Llvmir = 0,
- Bytecode = 1,
+ #[default]
+ Unset = 0,
+ Llvmir = 1,
+ Bitcode = 2,
+ Object = 3,
+ Executable = 4,
+}
+
+impl fmt::Display for CodeGenType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = match self {
+ CodeGenType::Unset => "CodeGen_Unset",
+ CodeGenType::Llvmir => "CodeGen_Llvmir",
+ CodeGenType::Bitcode => "CodeGen_Bitcode",
+ CodeGenType::Object => "CodeGen_Object",
+ CodeGenType::Executable => "CodeGen_Executable",
+ };
+ write!(f, "{}", s)
+ }
+}
+
+#[repr(u8)]
+#[derive(Clone,Copy,Default,PartialEq)]
+pub enum HostArch {
+ #[default]
+ Unknown = 0,
+ Aarch64 = 1,
+ X86 = 2,
+ X86_64 = 3,
+}
+
+impl fmt::Display for HostArch {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = match self {
+ HostArch::Unknown => "HostArch_Unknown",
+ HostArch::Aarch64 => "HostArch_Aarch64",
+ HostArch::X86 => "HostArch_X86",
+ HostArch::X86_64 => "HostArch_X86_64",
+ };
+ write!(f, "{}", s)
+ }
+}
+
+pub fn get_host_arch() -> HostArch {
+ if cfg!(target_arch = "aarch64") {
+ HostArch::Aarch64
+ } else if cfg!(target_arch = "x86") {
+ HostArch::X86
+ } else if cfg!(target_arch = "x86_64") {
+ HostArch::X86_64
+ } else {
+ HostArch::Unknown
+ }
+}
+
+#[repr(u8)]
+#[derive(Clone,Copy,Default,PartialEq)]
+pub enum HostOS {
+ #[default]
+ Unknown = 0,
+ Linux = 1,
+ MacOS = 2,
+ Windows = 3,
+}
+
+impl fmt::Display for HostOS {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let s = match self {
+ HostOS::Unknown => "HostOS_Unknown",
+ HostOS::Linux => "HostOS_Linux",
+ HostOS::MacOS => "HostOS_MacOS",
+ HostOS::Windows => "HostOS_Windows",
+ };
+ write!(f, "{}", s)
+ }
+}
+
+pub fn get_host_os() -> HostOS {
+ if cfg!(target_os = "linux") {
+ HostOS::Linux
+ } else if cfg!(target_os = "macos") {
+ HostOS::MacOS
+ } else if cfg!(target_os = "windows") {
+ HostOS::Windows
+ } else {
+ HostOS::Unknown
+ }
}
diff --git a/src/target.rs b/src/target.rs
index fc7af4d..bfd5de0 100644
--- a/src/target.rs
+++ b/src/target.rs
@@ -8,8 +8,31 @@ use llvm::error::LLVMDisposeErrorMessage;
use llvm::error::LLVMGetErrorMessage;
use llvm::core::LLVMDisposeMessage;
use llvm::core::LLVMSetTarget;
-use llvm::target::*;
-use llvm::target_machine::*;
+use llvm::target::LLVM_InitializeAllAsmParsers;
+use llvm::target::LLVM_InitializeAllAsmPrinters;
+use llvm::target::LLVM_InitializeAllTargetInfos;
+use llvm::target::LLVM_InitializeAllTargetMCs;
+use llvm::target::LLVM_InitializeAllTargets;
+use llvm::target::LLVMDisposeTargetData;
+use llvm::target::LLVMSetModuleDataLayout;
+use llvm::target::LLVMTargetDataRef;
+use llvm::target_machine::LLVMCreateTargetDataLayout;
+use llvm::target_machine::LLVMCreateTargetMachineOptions;
+use llvm::target_machine::LLVMCreateTargetMachineWithOptions;
+use llvm::target_machine::LLVMCodeGenOptLevel;
+use llvm::target_machine::LLVMCodeModel;
+use llvm::target_machine::LLVMDisposeTargetMachine;
+use llvm::target_machine::LLVMDisposeTargetMachineOptions;
+use llvm::target_machine::LLVMGetDefaultTargetTriple;
+use llvm::target_machine::LLVMGetFirstTarget;
+use llvm::target_machine::LLVMGetTargetFromTriple;
+use llvm::target_machine::LLVMRelocMode;
+use llvm::target_machine::LLVMTargetMachineOptionsRef;
+use llvm::target_machine::LLVMTargetMachineOptionsSetCodeGenOptLevel;
+use llvm::target_machine::LLVMTargetMachineOptionsSetCodeModel;
+use llvm::target_machine::LLVMTargetMachineOptionsSetRelocMode;
+use llvm::target_machine::LLVMTargetMachineRef;
+use llvm::target_machine::LLVMTargetRef;
use llvm::transforms::pass_builder::*;
@@ -24,7 +47,6 @@ use crate::options;
use exit_code::exit;
use exit_code::ExitCode;
use module::ModuleBundle;
-use options::opt_level_to_str;
use options::OptLevel;
static mut INITIALIZED: bool = false;
@@ -147,6 +169,13 @@ impl <'a> Drop for TargetMachine<'a> {
}
}
+#[allow(dead_code)]
+#[derive(Clone)]
+pub enum Passes {
+ Default(OptLevel),
+ Pipeline(String),
+}
+
pub struct PassBuilder {
builder: LLVMPassBuilderOptionsRef,
}
@@ -161,10 +190,18 @@ impl PassBuilder {
&mut self,
bundle: &mut ModuleBundle,
machine: &mut TargetMachine,
- opt_level: OptLevel,
+ passes: Passes,
no_target: bool,
) -> bool {
- let passes: String = format!("default<{}>\0", opt_level_to_str(opt_level));
+ let passes_str = match passes {
+ Passes::Default(opt_level) => format!("default<{}>\0", match opt_level {
+ OptLevel::O0 => "O0",
+ OptLevel::O1 => "O1",
+ OptLevel::O2 => "O2",
+ OptLevel::O3 => "O3",
+ }),
+ Passes::Pipeline(string) => format!("{}\0", string),
+ };
unsafe {
if !no_target {
LLVMSetModuleDataLayout(bundle.module, machine.data_layout);
@@ -172,7 +209,7 @@ impl PassBuilder {
};
let error: LLVMErrorRef = LLVMRunPasses(
bundle.module,
- passes.as_ptr() as *const c_char,
+ passes_str.as_ptr() as *const c_char,
machine.machine,
self.builder
);
diff --git a/tests/lit-llvm/codegen_type.calc b/tests/lit-llvm/codegen_type.calc
deleted file mode 100644
index 7b93b35..0000000
--- a/tests/lit-llvm/codegen_type.calc
+++ /dev/null
@@ -1,14 +0,0 @@
-// RUN: not @calcc --nomain --notarget -e "10" -S -o %t.bc 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: not @calcc --nomain --notarget -e "10" -o %t.ll 2>&1 | @filecheck %s --check-prefix=CHECK_B
-
-// RUN: @calcc --nomain --notarget -e "10" -S -o %t0.ll && @llvm-as -o %t0.bc %t0.ll
-// RUN: @calcc --nomain --notarget -e "10" -o %t1.bc && @llvm-dis -o %t1.ll %t1.bc
-// RUN: @count -l %t0.ll | @filecheck %s --check-prefix=CHECK_C
-// RUN: @count -l %t1.ll | @filecheck %s --check-prefix=CHECK_C
-// RUN: @count -l %t0.bc | @filecheck %s --check-prefix=CHECK_C
-// RUN: @count -l %t1.bc | @filecheck %s --check-prefix=CHECK_C
-
-// CHECK_A: LLVM IR file type (with '-S' flag) should match output name ('.ll' extension)
-// CHECK_B: Bytecode file type (missing '-S' flag) should match output name ('.bc' extension)
-
-// CHECK_C-NOT: ^0
diff --git a/tests/lit-llvm/end_to_end1.calc b/tests/lit-llvm/end_to_end1.calc
deleted file mode 100644
index 2c898e9..0000000
--- a/tests/lit-llvm/end_to_end1.calc
+++ /dev/null
@@ -1,9 +0,0 @@
-// RUN: @calcc -e "4096" -o %t0.bc && @clang -O0 -o %t0.out %t0.bc && %t0.out | @filecheck %s
-// RUN: @calcc -e "1024*32/8" -o %t1.bc && @clang -O0 -o %t1.out %t1.bc && %t1.out | @filecheck %s
-// RUN: @calcc -e "with: a: a*32/8" -o %t2.bc && @clang -O0 -o %t2.out %t2.bc && %t2.out 1024 | @filecheck %s
-// RUN: @calcc -e "with: a: 1024*a/8" -o %t3.bc && @clang -O0 -o %t3.out %t3.bc && %t3.out 32 | @filecheck %s
-// RUN: @calcc -e "with: a: 1024*32/a" -o %t4.bc && @clang -O0 -o %t4.out %t4.bc && %t4.out 8 | @filecheck %s
-
-// CHECK: calcc_main result: 4096
-
-// XFAIL: OS_MACOS
diff --git a/tests/lit-llvm/end_to_end2.calc b/tests/lit-llvm/end_to_end2.calc
deleted file mode 100644
index 26c8ee0..0000000
--- a/tests/lit-llvm/end_to_end2.calc
+++ /dev/null
@@ -1,49 +0,0 @@
-// RUN: @calcc -e "with: N1: N1 + 8" -o %t0.bc && @clang -O0 -o %t0.out %t0.bc && %t0.out 15 | @filecheck %s --check-prefix=CHECK_0
-// RUN: @calcc -e "with: N1: 8 + N1" -o %t1.bc && @clang -O0 -o %t1.out %t1.bc && %t1.out 15 | @filecheck %s --check-prefix=CHECK_1
-
-// RUN: @calcc -e "with: N1: N1 - 8" -o %t2.bc && @clang -O0 -o %t2.out %t2.bc && %t2.out 15 | @filecheck %s --check-prefix=CHECK_2
-// RUN: @calcc -e "with: N1: 8 - N1" -o %t3.bc && @clang -O0 -o %t3.out %t3.bc && %t3.out 15 | @filecheck %s --check-prefix=CHECK_3
-// RUN: @calcc -e "with: N1: -N1 + 8" -o %t4.bc && @clang -O0 -o %t4.out %t4.bc && %t4.out 15 | @filecheck %s --check-prefix=CHECK_4
-// RUN: @calcc -e "with: N1: 8 + -N1" -o %t5.bc && @clang -O0 -o %t5.out %t5.bc && %t5.out 15 | @filecheck %s --check-prefix=CHECK_5
-
-// RUN: @calcc -e "with: N1: N1 * 8" -o %t6.bc && @clang -O0 -o %t6.out %t6.bc && %t6.out 15 | @filecheck %s --check-prefix=CHECK_6
-// RUN: @calcc -e "with: N1: 8 * N1" -o %t7.bc && @clang -O0 -o %t7.out %t7.bc && %t7.out 15 | @filecheck %s --check-prefix=CHECK_7
-
-// RUN: @calcc -e "with: N1: -N1 * 8" -o %t8.bc && @clang -O0 -o %t8.out %t8.bc && %t8.out 15 | @filecheck %s --check-prefix=CHECK_8
-// RUN: @calcc -e "with: N1: 8 * -N1" -o %t9.bc && @clang -O0 -o %t9.out %t9.bc && %t9.out 15 | @filecheck %s --check-prefix=CHECK_9
-
-// RUN: @calcc -e "with: N1: N1 / 8" -o %tA.bc && @clang -O0 -o %tA.out %tA.bc && %tA.out 15 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc -e "with: N1: 8 / N1" -o %tB.bc && @clang -O0 -o %tB.out %tB.bc && %tB.out 15 | @filecheck %s --check-prefix=CHECK_B
-
-// RUN: @calcc -e "with: N1: -N1 / 8" -o %tC.bc && @clang -O0 -o %tC.out %tC.bc && %tC.out 15 | @filecheck %s --check-prefix=CHECK_C
-// RUN: @calcc -e "with: N1: 8 / -N1" -o %tD.bc && @clang -O0 -o %tD.out %tD.bc && %tD.out 15 | @filecheck %s --check-prefix=CHECK_D
-
-// RUN: @calcc -O0 -e "with: N1: -(N1*2) / 8" -o %tE0.bc && @clang -O0 -o %tE0.out %tE0.bc && %tE0.out 15 | @filecheck %s --check-prefix=CHECK_E
-// RUN: @calcc -e "with: N1: -(N1*2) / 8" -o %tE.bc && @clang -O0 -o %tE.out %tE.bc && %tE.out 15 | @filecheck %s --check-prefix=CHECK_E
-// RUN: @calcc -O0 -e "with: N1: 8 / -(N1*2)" -o %tF0.bc && @clang -O0 -o %tF0.out %tF0.bc && %tF0.out 15 | @filecheck %s --check-prefix=CHECK_F
-// RUN: @calcc -e "with: N1: 8 / -(N1*2)" -o %tF.bc && @clang -O0 -o %tF.out %tF.bc && %tF.out 15 | @filecheck %s --check-prefix=CHECK_F
-
-// CHECK_0: calcc_main result: 23
-// CHECK_1: calcc_main result: 23
-
-// CHECK_2: calcc_main result: 7
-// CHECK_3: calcc_main result: -7
-// CHECK_4: calcc_main result: -7
-// CHECK_5: calcc_main result: -7
-
-// CHECK_6: calcc_main result: 120
-// CHECK_7: calcc_main result: 120
-
-// CHECK_8: calcc_main result: -120
-// CHECK_9: calcc_main result: -120
-
-// CHECK_A: calcc_main result: 1
-// CHECK_B: calcc_main result: 0
-
-// CHECK_C: calcc_main result: -1
-// CHECK_D: calcc_main result: 0
-
-// CHECK_E: calcc_main result: -3
-// CHECK_F: calcc_main result: 0
-
-// XFAIL: OS_MACOS
diff --git a/tests/lit-llvm/lex_other.calc b/tests/lit-llvm/lex_other.calc
deleted file mode 100644
index 78bfb0c..0000000
--- a/tests/lit-llvm/lex_other.calc
+++ /dev/null
@@ -1,19 +0,0 @@
-// RUN: @calcc --verbose --lex -e ',:-()/*' 2>&1 | @filecheck %s
-
-// CHECK: Processing input 'Expression:,:-()/*'
-// CHECK: Read 7 bytes from buffer at line 0
-// CHECK: Found char ',' in line 0 at pos 0
-// CHECK: Lexed token 'Comma:,'
-// CHECK: Found char ':' in line 0 at pos 1
-// CHECK: Lexed token 'Colon::'
-// CHECK: Found char '-' in line 0 at pos 2
-// CHECK: Lexed token 'Minus:-'
-// CHECK: Found char '(' in line 0 at pos 3
-// CHECK: Lexed token 'ParenL:('
-// CHECK: Found char ')' in line 0 at pos 4
-// CHECK: Lexed token 'ParenR:)'
-// CHECK: Found char '/' in line 0 at pos 5
-// CHECK: Lexed token 'Slash:/'
-// CHECK: Found char '*' in line 0 at pos 6
-// CHECK: Lexed token 'Star:*'
-// CHECK: Lexed token 'Eoi:'
diff --git a/tests/lit-llvm/parse_eoi.calc b/tests/lit-llvm/parse_eoi.calc
deleted file mode 100644
index 9194ced..0000000
--- a/tests/lit-llvm/parse_eoi.calc
+++ /dev/null
@@ -1,21 +0,0 @@
-// RUN: not @calcc -v --parse -e "" 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: not @calcc -v --parse -e "25 + " 2>&1 | @filecheck %s --check-prefix=CHECK_B
-// RUN: not @calcc -v --parse -e "with: a" 2>&1 | @filecheck %s --check-prefix=CHECK_C
-// RUN: not @calcc -v --parse -e "with: a: a +" 2>&1 | @filecheck %s --check-prefix=CHECK_D
-// RUN: not @calcc -v --parse -e "// comment" 2>&1 | @filecheck %s --check-prefix=CHECK_E
-
-// CHECK_A: Lexed token 'Eoi:'
-// CHECK_A: Unexpected token
-
-// CHECK_B: Lexed token 'Eoi:'
-// CHECK_B: Unexpected token
-
-// CHECK_C: Lexed token 'Eoi:'
-// CHECK_C: Expected 'Colon' token at position 3
-
-// CHECK_D: Lexed token 'Eoi:'
-// CHECK_D: Unexpected token
-
-// CHECK_E: Lexed token 'Comment:// comment'
-// CHECK_E: Lexed token 'Eoi:'
-// CHECK_E: Unexpected token
diff --git a/tests/lit-llvm/parse_idents.calc b/tests/lit-llvm/parse_idents.calc
deleted file mode 100644
index a76e093..0000000
--- a/tests/lit-llvm/parse_idents.calc
+++ /dev/null
@@ -1,20 +0,0 @@
-// RUN: @calcc --verbose --ast --parse -e a 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc --verbose --ast --parse -e point 2>&1 | @filecheck %s --check-prefix=CHECK_B
-// RUN: @calcc --verbose --ast --parse -e Zpos 2>&1 | @filecheck %s --check-prefix=CHECK_C
-// RUN: @calcc --verbose --ast --parse -e __file__ 2>&1 | @filecheck %s --check-prefix=CHECK_D
-// RUN: @calcc --verbose --ast --parse -e tmp002 2>&1 | @filecheck %s --check-prefix=CHECK_E
-
-// CHECK_A: Consumed expected token 'Ident' at position '0'
-// CHECK_A: AST: Ident(a)
-
-// CHECK_B: Consumed expected token 'Ident' at position '0'
-// CHECK_B: AST: Ident(point)
-
-// CHECK_C: Consumed expected token 'Ident' at position '0'
-// CHECK_C: AST: Ident(Zpos)
-
-// CHECK_D: Consumed expected token 'Ident' at position '0'
-// CHECK_D: AST: Ident(__file__)
-
-// CHECK_E: Consumed expected token 'Ident' at position '0'
-// CHECK_E: AST: Ident(tmp002)
diff --git a/tests/lit-llvm/parse_numbers.calc b/tests/lit-llvm/parse_numbers.calc
deleted file mode 100644
index 6b6c0a9..0000000
--- a/tests/lit-llvm/parse_numbers.calc
+++ /dev/null
@@ -1,31 +0,0 @@
-// RUN: @calcc --verbose --ast --parse -e 256 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc --verbose --ast --parse -e 9 2>&1 | @filecheck %s --check-prefix=CHECK_B
-// RUN: @calcc --verbose --ast --parse -e -9 2>&1 | @filecheck %s --check-prefix=CHECK_C
-// RUN: @calcc --verbose --ast --parse -e "- 9" 2>&1 | @filecheck %s --check-prefix=CHECK_C
-// RUN: @calcc --verbose --ast --parse -e "0x7FFFFFFFFFFFFFFF" 2>&1 | @filecheck %s --check-prefix=CHECK_D
-
-// RUN: not @calcc --verbose --parse -e "0x8000000000000000" 2>&1 | @filecheck %s --check-prefix=CHECK_E
-// RUN: not @calcc --verbose --parse -e "9223372036854775808" 2>&1 | @filecheck %s --check-prefix=CHECK_F
-// RUN: not @calcc --verbose --parse -e "-9223372036854775809" 2>&1 | @filecheck %s --check-prefix=CHECK_G
-
-// CHECK_A: Consumed expected token 'Number' at position '0'
-// CHECK_A: AST: 256
-
-// CHECK_B: Consumed expected token 'Number' at position '0'
-// CHECK_B: AST: 9
-
-// CHECK_C: Consumed expected token 'Minus' at position '0'
-// CHECK_C: Consumed expected token 'Number' at position '1'
-// CHECK_C: AST: -9
-
-// CHECK_D: Consumed expected token 'Number' at position '0'
-// CHECK_D: AST: 9223372036854775807
-
-// CHECK_E: Number '0x8000000000000000' failed parse: number too large to fit in target type
-// CHECK_E: Failed to convert hexadecimal string
-
-// CHECK_F: Number '9223372036854775808' failed parse: number too large to fit in target type
-// CHECK_F: Failed to convert decimal string
-
-// CHECK_G: Number '-9223372036854775809' failed parse: number too small to fit in target type
-// CHECK_G: Failed to convert decimal string
diff --git a/tests/lit-rust/lex_hello.calc b/tests/lit-rust/lex_hello.calc
deleted file mode 100644
index e18fc8c..0000000
--- a/tests/lit-rust/lex_hello.calc
+++ /dev/null
@@ -1,14 +0,0 @@
-// RUN: @calcc --verbose --lex -e hello
-// RUN: @calcc --verbose --lex -e=hello
-// RUN: @calcc --verbose --lex --expr hello
-// RUN: @calcc --verbose --lex --expr=hello
-
-// CHECK: Processing input 'Expression:hello'
-// CHECK: Read 5 bytes from buffer at line 0
-// CHECK: Found char 'h' in line 0 at pos 0
-// CHECK: Found char 'e' in line 0 at pos 1
-// CHECK: Found char 'l' in line 0 at pos 2
-// CHECK: Found char 'l' in line 0 at pos 3
-// CHECK: Found char 'o' in line 0 at pos 4
-// CHECK: Lexed token 'Ident:hello'
-// CHECK: Lexed token 'Eoi:'
diff --git a/tests/lit-rust/lex_number.calc b/tests/lit-rust/lex_number.calc
deleted file mode 100644
index fa18b3f..0000000
--- a/tests/lit-rust/lex_number.calc
+++ /dev/null
@@ -1,9 +0,0 @@
-// RUN: @calcc --verbose --lex -e 256
-
-// CHECK: Processing input 'Expression:256'
-// CHECK: Read 3 bytes from buffer at line 0
-// CHECK: Found char '2' in line 0 at pos 0
-// CHECK: Found char '5' in line 0 at pos 1
-// CHECK: Found char '6' in line 0 at pos 2
-// CHECK: Lexed token 'Number:256'
-// CHECK: Lexed token 'Eoi:'
diff --git a/tests/lit-rust/usage.calc b/tests/lit-rust/usage.calc
deleted file mode 100644
index 81d7af4..0000000
--- a/tests/lit-rust/usage.calc
+++ /dev/null
@@ -1,4 +0,0 @@
-// RUN: @calcc -h
-// RUN: @calcc --help
-
-// CHECK: usage: calcc [OPTIONS]
diff --git a/tests/lit-rust/version.calc b/tests/lit-rust/version.calc
deleted file mode 100644
index 4ee8574..0000000
--- a/tests/lit-rust/version.calc
+++ /dev/null
@@ -1,5 +0,0 @@
-// RUN: @calcc --version
-
-// CHECK: Welcome to calcc version v0.1.0-dev
-// CHECK: // Copyright 2024, Giordano Salvador
-// CHECK: // SPDX-License-Identifier: BSD-3-Clause
diff --git a/tests/lit-tests-rust.rs b/tests/lit-tests-rust.rs
deleted file mode 100644
index 68eadef..0000000
--- a/tests/lit-tests-rust.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-extern crate lit;
-
-#[allow(dead_code)]
-#[cfg(test)]
-mod tests{
- use std::env;
- use std::path::Path;
- use std::path::PathBuf;
-
- fn path_to_string(path: &PathBuf) -> String {
- path.to_str().unwrap().to_string()
- }
-
- fn get_bin_dir() -> PathBuf {
- env::current_exe().ok().map(|mut path: PathBuf| {
- path.pop();
- path.pop();
- path
- }).unwrap()
- }
-
- fn get_calcc() -> String {
- path_to_string(&get_bin_dir().join(
- format!("{}{}", "calcc", env::consts::EXE_SUFFIX)
- ))
- }
-
- // Disabled -- #[test]
- fn lit() {
- lit::run::tests(
- lit::event_handler::Default::new(),
- |config: &mut lit::config::Config| {
- config.add_search_path(Path::new("tests").join("lit-rust").to_str().unwrap());
- config.add_extension("calc");
- config.constants.insert("calcc".to_owned(), get_calcc());
- }
- ).expect("Lit tests failed") ;
- }
-}
diff --git a/tests/lit-tests-llvm.rs b/tests/lit-tests.rs
similarity index 98%
rename from tests/lit-tests-llvm.rs
rename to tests/lit-tests.rs
index 350b5ba..ca57806 100644
--- a/tests/lit-tests-llvm.rs
+++ b/tests/lit-tests.rs
@@ -126,7 +126,7 @@ mod tests{
let lit_bin: &Path = Path::new(lit_bin_str.as_str());
let shell: String = get_shell(&os_name);
let tests_dir: PathBuf = get_tests_dir();
- let lit_dir: PathBuf = tests_dir.join("lit-llvm");
+ let lit_dir: PathBuf = tests_dir.join("lit-tests");
let cfg_path: PathBuf = lit_dir.join("lit.cfg");
println!("Lit binary path: {}", lit_bin_str);
diff --git a/tests/lit-tests/codegen_type.calc b/tests/lit-tests/codegen_type.calc
new file mode 100644
index 0000000..4097583
--- /dev/null
+++ b/tests/lit-tests/codegen_type.calc
@@ -0,0 +1,28 @@
+// RUN: not @calcc -k -e "10" -S -o %t.bc 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: not @calcc -k -e "10" -b -o %t.ll 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: not @calcc -k -e "10" -b -o %t.o 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: not @calcc -k -e "10" -b -o %t.exe 2>&1 | @filecheck %s --check-prefix=CHECK_D
+// RUN: not @calcc -k -e "10" -b -o %t 2>&1 | @filecheck %s --check-prefix=CHECK_E
+
+// RUN: @calcc -k -e "10" -S -o %t0.ll && @llvm-as -o %t0.bc %t0.ll
+// RUN: @calcc -k -e "10" -b -o %t1.bc && @llvm-dis -o %t1.ll %t1.bc
+// RUN: @calcc -e "10" -c -o %t1.o
+// RUN: @calcc -e "10" -o %t1.exe
+// RUN: @calcc -e "10" -o %t1
+// RUN: @count -l %t0.ll | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t1.ll | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t0.bc | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t1.bc | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t1.o | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t1.exe | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t1 | @filecheck %s --check-prefix=CHECK_F
+
+// UNSUPPORTED: OS_MACOS
+
+// CHECK_A: Output name ('.bc' extension) should match codegen type (-b|--bitcode)
+// CHECK_B: Output name ('.ll' extension) should match codegen type (-S|--llvmir)
+// CHECK_C: Output name ('.o' extension) should match codegen type (-c)
+// CHECK_D: Output name ('.exe' extension) should match codegen type: CodeGen_Bitcode specified
+// CHECK_E: Output name (no/unknown extension) should match codegen type: CodeGen_Bitcode specified
+
+// CHECK_F-NOT: ^0
diff --git a/tests/lit-tests/codegen_type_c.calc b/tests/lit-tests/codegen_type_c.calc
new file mode 100644
index 0000000..88bec94
--- /dev/null
+++ b/tests/lit-tests/codegen_type_c.calc
@@ -0,0 +1,26 @@
+// RUN: not @calcc -C -e "10" -S -o %t.bc 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: not @calcc -C -e "10" -b -o %t.ll 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: not @calcc -C -e "10" -b -o %t.o 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: not @calcc -C -e "10" -b -o %t.exe 2>&1 | @filecheck %s --check-prefix=CHECK_D
+// RUN: not @calcc -C -e "10" -b -o %t 2>&1 | @filecheck %s --check-prefix=CHECK_E
+
+// RUN: @calcc -C -e "10" -S -o %t0.ll && @llvm-as -o %t0.bc %t0.ll
+// RUN: @calcc -C -e "10" -b -o %t1.bc && @llvm-dis -o %t1.ll %t1.bc
+// RUN: @calcc -C -e "10" -c -o %t1.o
+// RUN: @calcc -C -e "10" -o %t1.exe
+// RUN: @calcc -C -e "10" -o %t1
+// RUN: @count -l %t0.ll | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t1.ll | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t0.bc | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t1.bc | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t1.o | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t1.exe | @filecheck %s --check-prefix=CHECK_F
+// RUN: @count -l %t1 | @filecheck %s --check-prefix=CHECK_F
+
+// CHECK_A: Output name ('.bc' extension) should match codegen type (-b|--bitcode)
+// CHECK_B: Output name ('.ll' extension) should match codegen type (-S|--llvmir)
+// CHECK_C: Output name ('.o' extension) should match codegen type (-c)
+// CHECK_D: Output name ('.exe' extension) should match codegen type: CodeGen_Bitcode specified
+// CHECK_E: Output name (no/unknown extension) should match codegen type: CodeGen_Bitcode specified
+
+// CHECK_F-NOT: ^0
diff --git a/tests/lit-tests/end_to_end1.calc b/tests/lit-tests/end_to_end1.calc
new file mode 100644
index 0000000..55529e1
--- /dev/null
+++ b/tests/lit-tests/end_to_end1.calc
@@ -0,0 +1,8 @@
+// RUN: @calcc -b -e "4096" -o %t0.bc && @clang -O0 -o %t0.out %t0.bc && %t0.out | @filecheck %s
+// RUN: @calcc -b -e "1024*32/8" -o %t1.bc && @clang -O0 -o %t1.out %t1.bc && %t1.out | @filecheck %s
+// RUN: @calcc -b -e "with: a: a*32/8" -o %t2.bc && @clang -O0 -o %t2.out %t2.bc && %t2.out 1024 | @filecheck %s
+// RUN: @calcc -b -e "with: a: 1024*a/8" -o %t3.bc && @clang -O0 -o %t3.out %t3.bc && %t3.out 32 | @filecheck %s
+// RUN: @calcc -b -e "with: a: 1024*32/a" -o %t4.bc && @clang -O0 -o %t4.out %t4.bc && %t4.out 8 | @filecheck %s
+
+// UNSUPPORTED: OS_MACOS
+// CHECK: calcc_main result: 4096
diff --git a/tests/lit-tests/end_to_end2.calc b/tests/lit-tests/end_to_end2.calc
new file mode 100644
index 0000000..5c22bd9
--- /dev/null
+++ b/tests/lit-tests/end_to_end2.calc
@@ -0,0 +1,49 @@
+// RUN: @calcc -b -e "with: N1: N1 + 8" -o %t0.bc && @clang -O0 -o %t0.out %t0.bc && %t0.out 15 | @filecheck %s --check-prefix=CHECK_0
+// RUN: @calcc -b -e "with: N1: 8 + N1" -o %t1.bc && @clang -O0 -o %t1.out %t1.bc && %t1.out 15 | @filecheck %s --check-prefix=CHECK_1
+
+// RUN: @calcc -b -e "with: N1: N1 - 8" -o %t2.bc && @clang -O0 -o %t2.out %t2.bc && %t2.out 15 | @filecheck %s --check-prefix=CHECK_2
+// RUN: @calcc -b -e "with: N1: 8 - N1" -o %t3.bc && @clang -O0 -o %t3.out %t3.bc && %t3.out 15 | @filecheck %s --check-prefix=CHECK_3
+// RUN: @calcc -b -e "with: N1: -N1 + 8" -o %t4.bc && @clang -O0 -o %t4.out %t4.bc && %t4.out 15 | @filecheck %s --check-prefix=CHECK_4
+// RUN: @calcc -b -e "with: N1: 8 + -N1" -o %t5.bc && @clang -O0 -o %t5.out %t5.bc && %t5.out 15 | @filecheck %s --check-prefix=CHECK_5
+
+// RUN: @calcc -b -e "with: N1: N1 * 8" -o %t6.bc && @clang -O0 -o %t6.out %t6.bc && %t6.out 15 | @filecheck %s --check-prefix=CHECK_6
+// RUN: @calcc -b -e "with: N1: 8 * N1" -o %t7.bc && @clang -O0 -o %t7.out %t7.bc && %t7.out 15 | @filecheck %s --check-prefix=CHECK_7
+
+// RUN: @calcc -b -e "with: N1: -N1 * 8" -o %t8.bc && @clang -O0 -o %t8.out %t8.bc && %t8.out 15 | @filecheck %s --check-prefix=CHECK_8
+// RUN: @calcc -b -e "with: N1: 8 * -N1" -o %t9.bc && @clang -O0 -o %t9.out %t9.bc && %t9.out 15 | @filecheck %s --check-prefix=CHECK_9
+
+// RUN: @calcc -b -e "with: N1: N1 / 8" -o %tA.bc && @clang -O0 -o %tA.out %tA.bc && %tA.out 15 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc -b -e "with: N1: 8 / N1" -o %tB.bc && @clang -O0 -o %tB.out %tB.bc && %tB.out 15 | @filecheck %s --check-prefix=CHECK_B
+
+// RUN: @calcc -b -e "with: N1: -N1 / 8" -o %tC.bc && @clang -O0 -o %tC.out %tC.bc && %tC.out 15 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc -b -e "with: N1: 8 / -N1" -o %tD.bc && @clang -O0 -o %tD.out %tD.bc && %tD.out 15 | @filecheck %s --check-prefix=CHECK_D
+
+// RUN: @calcc -O0 -b -e "with: N1: -(N1*2) / 8" -o %tE0.bc && @clang -O0 -o %tE0.out %tE0.bc && %tE0.out 15 | @filecheck %s --check-prefix=CHECK_E
+// RUN: @calcc -b -e "with: N1: -(N1*2) / 8" -o %tE.bc && @clang -O0 -o %tE.out %tE.bc && %tE.out 15 | @filecheck %s --check-prefix=CHECK_E
+// RUN: @calcc -O0 -b -e "with: N1: 8 / -(N1*2)" -o %tF0.bc && @clang -O0 -o %tF0.out %tF0.bc && %tF0.out 15 | @filecheck %s --check-prefix=CHECK_F
+// RUN: @calcc -b -e "with: N1: 8 / -(N1*2)" -o %tF.bc && @clang -O0 -o %tF.out %tF.bc && %tF.out 15 | @filecheck %s --check-prefix=CHECK_F
+
+// UNSUPPORTED: OS_MACOS
+
+// CHECK_0: calcc_main result: 23
+// CHECK_1: calcc_main result: 23
+
+// CHECK_2: calcc_main result: 7
+// CHECK_3: calcc_main result: -7
+// CHECK_4: calcc_main result: -7
+// CHECK_5: calcc_main result: -7
+
+// CHECK_6: calcc_main result: 120
+// CHECK_7: calcc_main result: 120
+
+// CHECK_8: calcc_main result: -120
+// CHECK_9: calcc_main result: -120
+
+// CHECK_A: calcc_main result: 1
+// CHECK_B: calcc_main result: 0
+
+// CHECK_C: calcc_main result: -1
+// CHECK_D: calcc_main result: 0
+
+// CHECK_E: calcc_main result: -3
+// CHECK_F: calcc_main result: 0
diff --git a/tests/lit-tests/end_to_end3.calc b/tests/lit-tests/end_to_end3.calc
new file mode 100644
index 0000000..59498b8
--- /dev/null
+++ b/tests/lit-tests/end_to_end3.calc
@@ -0,0 +1,8 @@
+// RUN: @calcc -e "4096" -o %t0.out && %t0.out | @filecheck %s
+// RUN: @calcc -e "1024*32/8" -o %t1.out && %t1.out | @filecheck %s
+// RUN: @calcc -e "with: a: a*32/8" -o %t2.out && %t2.out 1024 | @filecheck %s
+// RUN: @calcc -e "with: a: 1024*a/8" -o %t3.out && %t3.out 32 | @filecheck %s
+// RUN: @calcc -e "with: a: 1024*32/a" -o %t4.out && %t4.out 8 | @filecheck %s
+
+// UNSUPPORTED: OS_MACOS
+// CHECK: calcc_main result: 4096
diff --git a/tests/lit-tests/end_to_end4.calc b/tests/lit-tests/end_to_end4.calc
new file mode 100644
index 0000000..21b0b52
--- /dev/null
+++ b/tests/lit-tests/end_to_end4.calc
@@ -0,0 +1,49 @@
+// RUN: @calcc -e "with: N1: N1 + 8" -o %t0.out && %t0.out 15 | @filecheck %s --check-prefix=CHECK_0
+// RUN: @calcc -e "with: N1: 8 + N1" -o %t1.out && %t1.out 15 | @filecheck %s --check-prefix=CHECK_1
+
+// RUN: @calcc -e "with: N1: N1 - 8" -o %t2.out && %t2.out 15 | @filecheck %s --check-prefix=CHECK_2
+// RUN: @calcc -e "with: N1: 8 - N1" -o %t3.out && %t3.out 15 | @filecheck %s --check-prefix=CHECK_3
+// RUN: @calcc -e "with: N1: -N1 + 8" -o %t4.out && %t4.out 15 | @filecheck %s --check-prefix=CHECK_4
+// RUN: @calcc -e "with: N1: 8 + -N1" -o %t5.out && %t5.out 15 | @filecheck %s --check-prefix=CHECK_5
+
+// RUN: @calcc -e "with: N1: N1 * 8" -o %t6.out && %t6.out 15 | @filecheck %s --check-prefix=CHECK_6
+// RUN: @calcc -e "with: N1: 8 * N1" -o %t7.out && %t7.out 15 | @filecheck %s --check-prefix=CHECK_7
+
+// RUN: @calcc -e "with: N1: -N1 * 8" -o %t8.out && %t8.out 15 | @filecheck %s --check-prefix=CHECK_8
+// RUN: @calcc -e "with: N1: 8 * -N1" -o %t9.out && %t9.out 15 | @filecheck %s --check-prefix=CHECK_9
+
+// RUN: @calcc -e "with: N1: N1 / 8" -o %tA.out && %tA.out 15 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc -e "with: N1: 8 / N1" -o %tB.out && %tB.out 15 | @filecheck %s --check-prefix=CHECK_B
+
+// RUN: @calcc -e "with: N1: -N1 / 8" -o %tC.out && %tC.out 15 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc -e "with: N1: 8 / -N1" -o %tD.out && %tD.out 15 | @filecheck %s --check-prefix=CHECK_D
+
+// RUN: @calcc -O0 -e "with: N1: -(N1*2) / 8" -o %tE0.out && %tE0.out 15 | @filecheck %s --check-prefix=CHECK_E
+// RUN: @calcc -e "with: N1: -(N1*2) / 8" -o %tE.out && %tE.out 15 | @filecheck %s --check-prefix=CHECK_E
+// RUN: @calcc -O0 -e "with: N1: 8 / -(N1*2)" -o %tF0.out && %tF0.out 15 | @filecheck %s --check-prefix=CHECK_F
+// RUN: @calcc -e "with: N1: 8 / -(N1*2)" -o %tF.out && %tF.out 15 | @filecheck %s --check-prefix=CHECK_F
+
+// UNSUPPORTED: OS_MACOS
+
+// CHECK_0: calcc_main result: 23
+// CHECK_1: calcc_main result: 23
+
+// CHECK_2: calcc_main result: 7
+// CHECK_3: calcc_main result: -7
+// CHECK_4: calcc_main result: -7
+// CHECK_5: calcc_main result: -7
+
+// CHECK_6: calcc_main result: 120
+// CHECK_7: calcc_main result: 120
+
+// CHECK_8: calcc_main result: -120
+// CHECK_9: calcc_main result: -120
+
+// CHECK_A: calcc_main result: 1
+// CHECK_B: calcc_main result: 0
+
+// CHECK_C: calcc_main result: -1
+// CHECK_D: calcc_main result: 0
+
+// CHECK_E: calcc_main result: -3
+// CHECK_F: calcc_main result: 0
diff --git a/tests/lit-tests/end_to_end_c1.calc b/tests/lit-tests/end_to_end_c1.calc
new file mode 100644
index 0000000..4ad15f1
--- /dev/null
+++ b/tests/lit-tests/end_to_end_c1.calc
@@ -0,0 +1,7 @@
+// RUN: @calcc -C -e "4096" -o %t0.out && %t0.out | @filecheck %s
+// RUN: @calcc -C -e "1024*32/8" -o %t1.out && %t1.out | @filecheck %s
+// RUN: @calcc -C -e "with: a: a*32/8" -o %t2.out && %t2.out 1024 | @filecheck %s
+// RUN: @calcc -C -e "with: a: 1024*a/8" -o %t3.out && %t3.out 32 | @filecheck %s
+// RUN: @calcc -C -e "with: a: 1024*32/a" -o %t4.out && %t4.out 8 | @filecheck %s
+
+// CHECK: calcc_main result: 4096
diff --git a/tests/lit-tests/end_to_end_c2.calc b/tests/lit-tests/end_to_end_c2.calc
new file mode 100644
index 0000000..80a87c6
--- /dev/null
+++ b/tests/lit-tests/end_to_end_c2.calc
@@ -0,0 +1,47 @@
+// RUN: @calcc -C -e "with: N1: N1 + 8" -o %t0.out && %t0.out 15 | @filecheck %s --check-prefix=CHECK_0
+// RUN: @calcc -C -e "with: N1: 8 + N1" -o %t1.out && %t1.out 15 | @filecheck %s --check-prefix=CHECK_1
+
+// RUN: @calcc -C -e "with: N1: N1 - 8" -o %t2.out && %t2.out 15 | @filecheck %s --check-prefix=CHECK_2
+// RUN: @calcc -C -e "with: N1: 8 - N1" -o %t3.out && %t3.out 15 | @filecheck %s --check-prefix=CHECK_3
+// RUN: @calcc -C -e "with: N1: -N1 + 8" -o %t4.out && %t4.out 15 | @filecheck %s --check-prefix=CHECK_4
+// RUN: @calcc -C -e "with: N1: 8 + -N1" -o %t5.out && %t5.out 15 | @filecheck %s --check-prefix=CHECK_5
+
+// RUN: @calcc -C -e "with: N1: N1 * 8" -o %t6.out && %t6.out 15 | @filecheck %s --check-prefix=CHECK_6
+// RUN: @calcc -C -e "with: N1: 8 * N1" -o %t7.out && %t7.out 15 | @filecheck %s --check-prefix=CHECK_7
+
+// RUN: @calcc -C -e "with: N1: -N1 * 8" -o %t8.out && %t8.out 15 | @filecheck %s --check-prefix=CHECK_8
+// RUN: @calcc -C -e "with: N1: 8 * -N1" -o %t9.out && %t9.out 15 | @filecheck %s --check-prefix=CHECK_9
+
+// RUN: @calcc -C -e "with: N1: N1 / 8" -o %tA.out && %tA.out 15 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc -C -e "with: N1: 8 / N1" -o %tB.out && %tB.out 15 | @filecheck %s --check-prefix=CHECK_B
+
+// RUN: @calcc -C -e "with: N1: -N1 / 8" -o %tC.out && %tC.out 15 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc -C -e "with: N1: 8 / -N1" -o %tD.out && %tD.out 15 | @filecheck %s --check-prefix=CHECK_D
+
+// RUN: @calcc -O0 -C -e "with: N1: -(N1*2) / 8" -o %tE0.out && %tE0.out 15 | @filecheck %s --check-prefix=CHECK_E
+// RUN: @calcc -C -e "with: N1: -(N1*2) / 8" -o %tE.out && %tE.out 15 | @filecheck %s --check-prefix=CHECK_E
+// RUN: @calcc -O0 -C -e "with: N1: 8 / -(N1*2)" -o %tF0.out && %tF0.out 15 | @filecheck %s --check-prefix=CHECK_F
+// RUN: @calcc -C -e "with: N1: 8 / -(N1*2)" -o %tF.out && %tF.out 15 | @filecheck %s --check-prefix=CHECK_F
+
+// CHECK_0: calcc_main result: 23
+// CHECK_1: calcc_main result: 23
+
+// CHECK_2: calcc_main result: 7
+// CHECK_3: calcc_main result: -7
+// CHECK_4: calcc_main result: -7
+// CHECK_5: calcc_main result: -7
+
+// CHECK_6: calcc_main result: 120
+// CHECK_7: calcc_main result: 120
+
+// CHECK_8: calcc_main result: -120
+// CHECK_9: calcc_main result: -120
+
+// CHECK_A: calcc_main result: 1
+// CHECK_B: calcc_main result: 0
+
+// CHECK_C: calcc_main result: -1
+// CHECK_D: calcc_main result: 0
+
+// CHECK_E: calcc_main result: -3
+// CHECK_F: calcc_main result: 0
diff --git a/tests/lit-llvm/input_file1.calc b/tests/lit-tests/input_file1.calc
similarity index 85%
rename from tests/lit-llvm/input_file1.calc
rename to tests/lit-tests/input_file1.calc
index 614174e..896a1c5 100644
--- a/tests/lit-llvm/input_file1.calc
+++ b/tests/lit-tests/input_file1.calc
@@ -3,11 +3,8 @@
// RUN: @calcc -S -O0 %s -o %t2.ll && @clang -O0 -o %t2.out %t2.ll && %t2.out 6 8 10 | @filecheck %s
// RUN: @calcc -S -O3 %s -o %t3.ll && @clang -O0 -o %t3.out %t3.ll && %t3.out 6 8 10 | @filecheck %s
-// CHECK: calcc_main result: 0
-
-// TODO: Comments are not implemented
+// UNSUPPORTED: OS_MACOS
+// CHECK: calcc_main result: 0
// Implement Pythagorean theorem check (for integer triangles): 0 result => passing check
with: a,b,c: c*c - (a*a + b*b)
-
-// XFAIL: OS_MACOS
diff --git a/tests/lit-tests/input_file_c1.calc b/tests/lit-tests/input_file_c1.calc
new file mode 100644
index 0000000..aa25757
--- /dev/null
+++ b/tests/lit-tests/input_file_c1.calc
@@ -0,0 +1,9 @@
+// RUN: @calcc -C -O0 %s -o %t0.out && %t0.out 3 4 5 | @filecheck %s
+// RUN: @calcc -C -O3 %s -o %t1.out && %t1.out 3 4 5 | @filecheck %s
+// RUN: @calcc -C -O0 %s -o %t2.out && %t2.out 6 8 10 | @filecheck %s
+// RUN: @calcc -C -O3 %s -o %t3.out && %t3.out 6 8 10 | @filecheck %s
+
+// CHECK: calcc_main result: 0
+
+// Implement Pythagorean theorem check (for integer triangles): 0 result => passing check
+with: a,b,c: c*c - (a*a + b*b)
diff --git a/tests/lit-llvm/irgen1.calc b/tests/lit-tests/irgen1.calc
similarity index 73%
rename from tests/lit-llvm/irgen1.calc
rename to tests/lit-tests/irgen1.calc
index 5a54f37..73d4d33 100644
--- a/tests/lit-llvm/irgen1.calc
+++ b/tests/lit-tests/irgen1.calc
@@ -1,8 +1,8 @@
-// RUN: @calcc --nomain -O0 --llvmir -e "with: a,b: a+b + 10" | @filecheck %s
-// RUN: @calcc --nomain -O0 -S -e "with: a,b: a+b + 10" | @filecheck %s
+// RUN: @calcc --no-main -O0 --llvmir -e "with: a,b: a+b + 10" | @filecheck %s
+// RUN: @calcc --no-main -O0 -S -e "with: a,b: a+b + 10" | @filecheck %s
// CHECK-LABEL: ; ModuleID = 'calcc'
-// CHECK: source_filename = "calcc"
+// CHECK: source_filename = "-"
// CHECK-LABEL: define i64 @calcc_main(i64 %0, i64 %1) {
// CHECK-LABEL: entry:
diff --git a/tests/lit-llvm/irgen2.calc b/tests/lit-tests/irgen2.calc
similarity index 62%
rename from tests/lit-llvm/irgen2.calc
rename to tests/lit-tests/irgen2.calc
index 79e7148..41a74f3 100644
--- a/tests/lit-llvm/irgen2.calc
+++ b/tests/lit-tests/irgen2.calc
@@ -1,11 +1,11 @@
-// RUN: @calcc --nomain -O0 --llvmir -e "1024*32/8" | @filecheck %s
-// RUN: @calcc --nomain -O0 -S -e "1024*32/8" | @filecheck %s
+// RUN: @calcc --no-main -O0 --llvmir -e "1024*32/8" | @filecheck %s
+// RUN: @calcc --no-main -O0 -S -e "1024*32/8" | @filecheck %s
/// The comprising arithmetic instructions are being optimized out
/// (constant folding) between insertion in the module and the module dump call.
// CHECK-LABEL: ; ModuleID = 'calcc'
-// CHECK: source_filename = "calcc"
+// CHECK: source_filename = "-"
// CHECK-LABEL: define i64 @calcc_main() {
// CHECK-LABEL: entry:
diff --git a/tests/lit-llvm/lex_comment.calc b/tests/lit-tests/lex_comment.calc
similarity index 88%
rename from tests/lit-llvm/lex_comment.calc
rename to tests/lit-tests/lex_comment.calc
index 8124bfd..e1c7540 100644
--- a/tests/lit-llvm/lex_comment.calc
+++ b/tests/lit-tests/lex_comment.calc
@@ -1,6 +1,6 @@
-// RUN: @calcc -v --lex -e "//" 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc -v --lex -e "// comment" 2>&1 | @filecheck %s --check-prefix=CHECK_B
-// RUN: @calcc -v --lex -e "with: a: 3/a // Divide by a" 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc -v -k -S --lex -e "//" 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc -v -k -S --lex -e "// comment" 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc -v -k -S --lex -e "with: a: 3/a // Divide by a" 2>&1 | @filecheck %s --check-prefix=CHECK_C
// CHECK_A: Processing input 'Expression://'
// CHECK_A: Read 2 bytes from buffer at line 0
diff --git a/tests/lit-llvm/lex_drop.calc b/tests/lit-tests/lex_drop.calc
similarity index 81%
rename from tests/lit-llvm/lex_drop.calc
rename to tests/lit-tests/lex_drop.calc
index 981d1e9..f66af49 100644
--- a/tests/lit-llvm/lex_drop.calc
+++ b/tests/lit-tests/lex_drop.calc
@@ -1,5 +1,5 @@
-// RUN: echo -n "hello $" | @calcc --verbose --lex --drop - 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: echo -n "hello é" | not @calcc --verbose --lex --drop - 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: echo -n "hello $" | @calcc --verbose -k -S --lex --drop - 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: echo -n "hello é" | not @calcc --verbose -k -S --lex --drop - 2>&1 | @filecheck %s --check-prefix=CHECK_B
// CHECK_A: Processing input 'Stdin'
// CHECK_A: Read 7 bytes from buffer at line 0
diff --git a/tests/lit-llvm/lex_hello.calc b/tests/lit-tests/lex_hello.calc
similarity index 57%
rename from tests/lit-llvm/lex_hello.calc
rename to tests/lit-tests/lex_hello.calc
index 0fff8c0..b868670 100644
--- a/tests/lit-llvm/lex_hello.calc
+++ b/tests/lit-tests/lex_hello.calc
@@ -1,7 +1,7 @@
-// RUN: @calcc --verbose --lex -e hello 2>&1 | @filecheck %s
-// RUN: @calcc --verbose --lex -e=hello 2>&1 | @filecheck %s
-// RUN: @calcc --verbose --lex --expr hello 2>&1 | @filecheck %s
-// RUN: @calcc --verbose --lex --expr=hello 2>&1 | @filecheck %s
+// RUN: @calcc --verbose -k -S --lex -e hello 2>&1 | @filecheck %s
+// RUN: @calcc --verbose -k -S --lex -e=hello 2>&1 | @filecheck %s
+// RUN: @calcc --verbose -k -S --lex --expr hello 2>&1 | @filecheck %s
+// RUN: @calcc --verbose -k -S --lex --expr=hello 2>&1 | @filecheck %s
// CHECK: Processing input 'Expression:hello'
// CHECK: Read 5 bytes from buffer at line 0
diff --git a/tests/lit-llvm/lex_idents.calc b/tests/lit-tests/lex_idents.calc
similarity index 75%
rename from tests/lit-llvm/lex_idents.calc
rename to tests/lit-tests/lex_idents.calc
index 1f6f484..d3ae159 100644
--- a/tests/lit-llvm/lex_idents.calc
+++ b/tests/lit-tests/lex_idents.calc
@@ -1,10 +1,10 @@
-// RUN: @calcc --verbose --lex -e "N" 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc --verbose --lex -e "_N" 2>&1 | @filecheck %s --check-prefix=CHECK_B
-// RUN: @calcc --verbose --lex -e "N0" 2>&1 | @filecheck %s --check-prefix=CHECK_C
-// RUN: @calcc --verbose --lex -e "N_0" 2>&1 | @filecheck %s --check-prefix=CHECK_D
-// RUN: @calcc --verbose --lex -e "_N0" 2>&1 | @filecheck %s --check-prefix=CHECK_E
-// RUN: @calcc --verbose --lex -e "_N_0" 2>&1 | @filecheck %s --check-prefix=CHECK_F
-// RUN: @calcc --verbose --lex -e "-N" 2>&1 | @filecheck %s --check-prefix=CHECK_G
+// RUN: @calcc --verbose -k -S --lex -e "N" 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc --verbose -k -S --lex -e "_N" 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc --verbose -k -S --lex -e "N0" 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc --verbose -k -S --lex -e "N_0" 2>&1 | @filecheck %s --check-prefix=CHECK_D
+// RUN: @calcc --verbose -k -S --lex -e "_N0" 2>&1 | @filecheck %s --check-prefix=CHECK_E
+// RUN: @calcc --verbose -k -S --lex -e "_N_0" 2>&1 | @filecheck %s --check-prefix=CHECK_F
+// RUN: @calcc --verbose -k -S --lex -e "-N" 2>&1 | @filecheck %s --check-prefix=CHECK_G
// CHECK_A: Processing input 'Expression:N'
// CHECK_A: Read 1 bytes from buffer at line 0
diff --git a/tests/lit-llvm/lex_nodrop.calc b/tests/lit-tests/lex_nodrop.calc
similarity index 81%
rename from tests/lit-llvm/lex_nodrop.calc
rename to tests/lit-tests/lex_nodrop.calc
index 0ab6ccd..6fcb3e0 100644
--- a/tests/lit-llvm/lex_nodrop.calc
+++ b/tests/lit-tests/lex_nodrop.calc
@@ -1,5 +1,5 @@
-// RUN: echo -n "hello $" | not @calcc --verbose --lex - 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: echo -n "hello é" | not @calcc --verbose --lex - 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: echo -n "hello $" | not @calcc --verbose -k -S --lex - 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: echo -n "hello é" | not @calcc --verbose -k -S --lex - 2>&1 | @filecheck %s --check-prefix=CHECK_B
// CHECK_A: Processing input 'Stdin'
// CHECK_A: Read 7 bytes from buffer at line 0
diff --git a/tests/lit-llvm/lex_number.calc b/tests/lit-tests/lex_number.calc
similarity index 79%
rename from tests/lit-llvm/lex_number.calc
rename to tests/lit-tests/lex_number.calc
index 67c3f61..79ff9df 100644
--- a/tests/lit-llvm/lex_number.calc
+++ b/tests/lit-tests/lex_number.calc
@@ -1,5 +1,5 @@
-// RUN: @calcc --verbose --lex -e 256 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc --verbose --lex -e 10002 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc --verbose --lex -k -S -e 256 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc --verbose --lex -k -S -e 10002 2>&1 | @filecheck %s --check-prefix=CHECK_B
// CHECK_A: Processing input 'Expression:256'
// CHECK_A: Read 3 bytes from buffer at line 0
diff --git a/tests/lit-llvm/lex_number_enhanced.calc b/tests/lit-tests/lex_number_enhanced.calc
similarity index 77%
rename from tests/lit-llvm/lex_number_enhanced.calc
rename to tests/lit-tests/lex_number_enhanced.calc
index 771ed62..9abb995 100644
--- a/tests/lit-llvm/lex_number_enhanced.calc
+++ b/tests/lit-tests/lex_number_enhanced.calc
@@ -1,10 +1,10 @@
-// RUN: @calcc --verbose --lex -e -9 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc --verbose --lex -e "- 9" 2>&1 | @filecheck %s --check-prefix=CHECK_B
-// RUN: @calcc --verbose --lex -e 0xAF 2>&1 | @filecheck %s --check-prefix=CHECK_C
-// RUN: @calcc --verbose --lex -e 0x0c1 2>&1 | @filecheck %s --check-prefix=CHECK_D
-// RUN: @calcc --verbose --lex -e 012 2>&1 | @filecheck %s --check-prefix=CHECK_E
-// RUN: not @calcc --verbose --lex -e 0x0z3 2>&1 | @filecheck %s --check-prefix=CHECK_F
-// RUN: not @calcc --verbose --lex -e 140_3 2>&1 | @filecheck %s --check-prefix=CHECK_G
+// RUN: @calcc --verbose -k -S --lex -e -9 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc --verbose -k -S --lex -e "- 9" 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc --verbose -k -S --lex -e 0xAF 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc --verbose -k -S --lex -e 0x0c1 2>&1 | @filecheck %s --check-prefix=CHECK_D
+// RUN: @calcc --verbose -k -S --lex -e 012 2>&1 | @filecheck %s --check-prefix=CHECK_E
+// RUN: not @calcc --verbose -k -S --lex -e 0x0z3 2>&1 | @filecheck %s --check-prefix=CHECK_F
+// RUN: not @calcc --verbose -k -S --lex -e 140_3 2>&1 | @filecheck %s --check-prefix=CHECK_G
// CHECK_A: Processing input 'Expression:-9'
// CHECK_A: Read 2 bytes from buffer at line 0
diff --git a/tests/lit-rust/lex_other.calc b/tests/lit-tests/lex_other.calc
similarity index 90%
rename from tests/lit-rust/lex_other.calc
rename to tests/lit-tests/lex_other.calc
index 92975eb..8b3f846 100644
--- a/tests/lit-rust/lex_other.calc
+++ b/tests/lit-tests/lex_other.calc
@@ -1,4 +1,4 @@
-// RUN: @calcc --verbose --lex -e ',:-()/*'
+// RUN: @calcc --verbose --lex -k -S -e ',:-()/*' 2>&1 | @filecheck %s
// CHECK: Processing input 'Expression:,:-()/*'
// CHECK: Read 7 bytes from buffer at line 0
diff --git a/tests/lit-llvm/lex_stdin.calc b/tests/lit-tests/lex_stdin.calc
similarity index 82%
rename from tests/lit-llvm/lex_stdin.calc
rename to tests/lit-tests/lex_stdin.calc
index a985ccc..efbf6bb 100644
--- a/tests/lit-llvm/lex_stdin.calc
+++ b/tests/lit-tests/lex_stdin.calc
@@ -1,4 +1,4 @@
-// RUN: echo -n "hello" | @calcc --verbose --lex - 2>&1 | @filecheck %s
+// RUN: echo -n "hello" | @calcc --verbose -k -S --lex - 2>&1 | @filecheck %s
// CHECK: Processing input 'Stdin'
// CHECK: Read 5 bytes from buffer at line 0
diff --git a/tests/lit-llvm/lex_trailing_whitespace.calc b/tests/lit-tests/lex_trailing_whitespace.calc
similarity index 90%
rename from tests/lit-llvm/lex_trailing_whitespace.calc
rename to tests/lit-tests/lex_trailing_whitespace.calc
index 49c8ad1..d3d6241 100644
--- a/tests/lit-llvm/lex_trailing_whitespace.calc
+++ b/tests/lit-tests/lex_trailing_whitespace.calc
@@ -1,5 +1,5 @@
-// RUN: @calcc -v --lex -e "10 + 5 " 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc -v --lex -e "with: a: 3 + a " 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc -v --lex -k -S -e "10 + 5 " 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc -v --lex -k -S -e "with: a: 3 + a " 2>&1 | @filecheck %s --check-prefix=CHECK_B
// CHECK_A: Processing input 'Expression:10 + 5 '
// CHECK_A: Read 8 bytes from buffer at line 0
diff --git a/tests/lit-llvm/lex_withdecl.calc b/tests/lit-tests/lex_withdecl.calc
similarity index 91%
rename from tests/lit-llvm/lex_withdecl.calc
rename to tests/lit-tests/lex_withdecl.calc
index a3d3b11..2f4cbf0 100644
--- a/tests/lit-llvm/lex_withdecl.calc
+++ b/tests/lit-tests/lex_withdecl.calc
@@ -1,6 +1,6 @@
-// RUN: @calcc --verbose --lex -e "with: a,b:a+b" 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc --verbose --lex -e "with : a , b: a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_B
-// RUN: @calcc --verbose --lex -e "with: a,b:a*a/b" 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc --verbose --lex -k -S -e "with: a,b:a+b" 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc --verbose --lex -k -S -e "with : a , b: a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc --verbose --lex -k -S -e "with: a,b:a*a/b" 2>&1 | @filecheck %s --check-prefix=CHECK_C
// CHECK_A: Processing input 'Expression:with: a,b:a+b'
// CHECK_A: Read 13 bytes from buffer at line 0
diff --git a/tests/lit-llvm/lit.cfg b/tests/lit-tests/lit.cfg
similarity index 86%
rename from tests/lit-llvm/lit.cfg
rename to tests/lit-tests/lit.cfg
index 7d875f5..59c963a 100644
--- a/tests/lit-llvm/lit.cfg
+++ b/tests/lit-tests/lit.cfg
@@ -25,6 +25,10 @@ if "OS_NAME" in lit_config.params:
config.available_features.add("OS_MACOS")
elif os_name == "windows":
config.available_features.add("OS_WINDOWS")
+ else:
+ config.available_features.add("OS_UNKNOWN")
+else:
+ config.available_features.add("OS_UNKNOWN")
if "ARCH" in lit_config.params:
arch = lit_config.params["ARCH"]
@@ -34,6 +38,12 @@ if "ARCH" in lit_config.params:
config.available_features.add("ARCH_X86_64")
elif arch == "aarch64":
config.available_features.add("ARCH_AARCH64")
+ else:
+ config.available_features.add("ARCH_UNKNOWN")
+else:
+ config.available_features.add("ARCH_UNKNOWN")
+
+print(f'Features configured: {", ".join([f for f in config.available_features])}\n')
config.test_format = lit.formats.ShTest(False)
config.test_source_root = os.path.dirname(__file__)
diff --git a/tests/lit-llvm/maingen1.calc b/tests/lit-tests/maingen1.calc
similarity index 98%
rename from tests/lit-llvm/maingen1.calc
rename to tests/lit-tests/maingen1.calc
index 5f64b6c..bc08245 100644
--- a/tests/lit-llvm/maingen1.calc
+++ b/tests/lit-tests/maingen1.calc
@@ -1,8 +1,10 @@
// RUN: @calcc -O0 --llvmir -e "with: a,b: a+b + 10" | @filecheck %s
// RUN: @calcc -O0 -S -e "with: a,b: a+b + 10" | @filecheck %s
+// UNSUPPORTED: OS_MACOS
+
// CHECK-LABEL: ; ModuleID = 'main'
-// CHECK: source_filename = "main"
+// CHECK: source_filename = "-"
// CHECK: @stderr = external global ptr, align 8
// CHECK: @.str.argerr = private unnamed_addr constant [50 x i8] c"Invalid number of args to main. Expected %d args\0A\00", align 1
diff --git a/tests/lit-llvm/maingen2.calc b/tests/lit-tests/maingen2.calc
similarity index 97%
rename from tests/lit-llvm/maingen2.calc
rename to tests/lit-tests/maingen2.calc
index 2382db4..9ebb9f7 100644
--- a/tests/lit-llvm/maingen2.calc
+++ b/tests/lit-tests/maingen2.calc
@@ -1,8 +1,10 @@
// RUN: @calcc -O0 --llvmir -e "1024*32/8" | @filecheck %s
// RUN: @calcc -O0 -S -e "1024*32/8" | @filecheck %s
+// UNSUPPORTED: OS_MACOS
+
// CHECK-LABEL: ; ModuleID = 'main'
-// CHECK: source_filename = "main"
+// CHECK: source_filename = "-"
// CHECK: @stderr = external global ptr, align 8
// CHECK: @.str.argerr = private unnamed_addr constant [50 x i8] c"Invalid number of args to main. Expected %d args\0A\00", align 1
diff --git a/tests/lit-tests/option_c_main.calc b/tests/lit-tests/option_c_main.calc
new file mode 100644
index 0000000..7f9ffbf
--- /dev/null
+++ b/tests/lit-tests/option_c_main.calc
@@ -0,0 +1,60 @@
+// RUN: not @calcc --llvmir %s --no-main --c-main 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: not @calcc --llvmir %s --c-main --no-main 2>&1 | @filecheck %s --check-prefix=CHECK_A
+
+// RUN: @calcc -v --ir %s --c-main 2>&1 | @filecheck %s --check-prefix=CHECK_B
+
+// RUN: @calcc --llvmir %s -O0 --c-main 2>&1 | @filecheck %s --check-prefix=CHECK_C
+
+// CHECK_A: Incompatible compiler flags: '-k|--no-main' and '-C|--c-main'
+
+// CHECK_B: #include
+// CHECK_B: #include
+
+// CHECK_B: #define BASE 1
+// CHECK_B: #define NUM_ARGS 2
+// CHECK_B: #define USAGE " , \n"
+
+// CHECK_B: typedef long long t_i64;
+// CHECK_B: extern t_i64 calcc_main(i64, i64);
+
+// CHECK_B: int main(int argc, char **argv) {
+// CHECK_B: if (argc != BASE + NUM_ARGS) {
+// CHECK_B: (void) fprintf(stderr, "Invalid number of args to main. Expected %d args\n", NUM_ARGS);
+// CHECK_B: (void) fprintf(stderr, USAGE);
+// CHECK_B: return 1;
+// CHECK_B: }
+
+// CHECK_B: /* Parameter declaration section: */
+// CHECK_B: const i64 p0 = (i64)atoll(argv[BASE + 0]);
+// CHECK_B: const i64 p1 = (i64)atoll(argv[BASE + 1]);
+
+// CHECK_B: /* Function call section: */
+// CHECK_B: const t_i64 result = calcc_main(p0, p1);
+// CHECK_B: (void) printf("calcc_main result: %lld\n", result);
+
+// CHECK_B: return 0;
+// CHECK_B: }
+
+// CHECK_C-LABEL: ; ModuleID = 'calcc'
+
+// CHECK_C-LABEL: define i64 @calcc_main(i64 %0, i64 %1) {
+// CHECK_C-LABEL: entry:
+// CHECK_C: %a = alloca i64, align 8
+// CHECK_C: store i64 %0, ptr %a, align 4
+// CHECK_C: %b = alloca i64, align 8
+// CHECK_C: store i64 %1, ptr %b, align 4
+// CHECK_C: %v0 = load i64, ptr %a, align 4
+// CHECK_C: %v1 = load i64, ptr %a, align 4
+// CHECK_C: %v2 = mul nsw i64 %v0, %v1
+// CHECK_C: %v3 = load i64, ptr %b, align 4
+// CHECK_C: %v4 = mul nsw i64 2, %v3
+// CHECK_C: %v5 = add nsw i64 %v2, %v4
+// CHECK_C: %v6 = sub nsw i64 %v5, 3
+// CHECK_C: ret i64 %v6
+// CHECK_C: }
+
+// COM: CHECK_C-LABEL: i32 define @main(i32 %vArgc, ptr %vArgv)
+// COM: CHECK_C-LABEL: entry:
+// COM: CHECK_C: %[[REG0:.+]] = call i64 @calcc_main(i64 %[[REG1:.+]], i64 %[[REG2:.+]])
+
+with: a,b: a*a + 2*b - 3
diff --git a/tests/lit-llvm/option_notarget.calc b/tests/lit-tests/option_notarget.calc
similarity index 69%
rename from tests/lit-llvm/option_notarget.calc
rename to tests/lit-tests/option_notarget.calc
index 4d72ae4..f2e9fed 100644
--- a/tests/lit-llvm/option_notarget.calc
+++ b/tests/lit-tests/option_notarget.calc
@@ -1,8 +1,8 @@
-// RUN: @calcc --nomain -O0 --llvmir -e "4096" | @filecheck %s --check-prefix=CHECK_TARGET
-// RUN: @calcc --nomain --notarget -O0 --llvmir -e "4096" | @filecheck %s --check-prefix=CHECK_NOTARGET
+// RUN: @calcc --no-main -O0 --llvmir -e "4096" | @filecheck %s --check-prefix=CHECK_TARGET
+// RUN: @calcc --no-main --notarget -O0 --llvmir -e "4096" | @filecheck %s --check-prefix=CHECK_NOTARGET
// CHECK_TARGET-LABEL: ; ModuleID = 'calcc'
-// CHECK_TARGET: source_filename = "calcc"
+// CHECK_TARGET: source_filename = "-"
// CHECK_TARGET: target datalayout = "{{[a-zA-Z0-9\:\-]+}}"
// CHECK_TARGET: target triple = "{{[_\.a-zA-Z0-9\-]+}}"
@@ -12,7 +12,7 @@
// CHECK_TARGET: }
// CHECK_NOTARGET-LABEL: ; ModuleID = 'calcc'
-// CHECK_NOTARGET: source_filename = "calcc"
+// CHECK_NOTARGET: source_filename = "-"
// CHECK_NOTARGET-NOT: target datalayout = "{{[a-zA-Z0-9\:\-]+}}"
// CHECK_NOTARGET-NOT: target triple = "{{[_\.a-zA-Z0-9\-]+}}"
diff --git a/tests/lit-llvm/option_opt.calc b/tests/lit-tests/option_opt.calc
similarity index 60%
rename from tests/lit-llvm/option_opt.calc
rename to tests/lit-tests/option_opt.calc
index 862491f..f03270c 100644
--- a/tests/lit-llvm/option_opt.calc
+++ b/tests/lit-tests/option_opt.calc
@@ -1,8 +1,9 @@
-// RUN: @calcc --nomain -O0 --llvmir -e "with: a: a*32" | @filecheck %s --check-prefix=CHECK_O0
-// RUN: @calcc --nomain -O1 --llvmir -e "with: a: a*32" | @filecheck %s --check-prefix=CHECK_O1
+// RUN: @calcc --no-main -O0 --llvmir -e "with: a: a*32" | @filecheck %s --check-prefix=CHECK_O0
+// RUN: @calcc --no-main -O1 --llvmir -e "with: a: a*32" | @filecheck %s --check-prefix=CHECK_O1
+// RUN: @calcc --no-main -O1 --llvmir --ir -e "with: a: a*32" 2>&1 | @filecheck %s --check-prefix=CHECK_O0
// CHECK_O0-LABEL: ; ModuleID = 'calcc'
-// CHECK_O0: source_filename = "calcc"
+// CHECK_O0: source_filename = "-"
// CHECK_O0-LABEL: define i64 @calcc_main(i64 %0) {
// CHECK_O0-LABEL: entry:
@@ -14,7 +15,7 @@
// CHECK_O0: }
// CHECK_O1-LABEL: ; ModuleID = 'calcc'
-// CHECK_O1: source_filename = "calcc"
+// CHECK_O1: source_filename = "-"
// CHECK_O1-LABEL: define i64 @calcc_main(i64 %0) {{.*}} {
// CHECK_O1-LABEL: entry:
diff --git a/tests/lit-tests/parse_eoi.calc b/tests/lit-tests/parse_eoi.calc
new file mode 100644
index 0000000..2795e5a
--- /dev/null
+++ b/tests/lit-tests/parse_eoi.calc
@@ -0,0 +1,21 @@
+// RUN: not @calcc -v -k -S --parse -e "" 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: not @calcc -v -k -S --parse -e "25 + " 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: not @calcc -v -k -S --parse -e "with: a" 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: not @calcc -v -k -S --parse -e "with: a: a +" 2>&1 | @filecheck %s --check-prefix=CHECK_D
+// RUN: not @calcc -v -k -S --parse -e "// comment" 2>&1 | @filecheck %s --check-prefix=CHECK_E
+
+// CHECK_A: Lexed token 'Eoi:'
+// CHECK_A: Unexpected token
+
+// CHECK_B: Lexed token 'Eoi:'
+// CHECK_B: Unexpected token
+
+// CHECK_C: Lexed token 'Eoi:'
+// CHECK_C: Expected 'Colon' token at position 3
+
+// CHECK_D: Lexed token 'Eoi:'
+// CHECK_D: Unexpected token
+
+// CHECK_E: Lexed token 'Comment:// comment'
+// CHECK_E: Lexed token 'Eoi:'
+// CHECK_E: Unexpected token
diff --git a/tests/lit-llvm/parse_expr_arith.calc b/tests/lit-tests/parse_expr_arith.calc
similarity index 76%
rename from tests/lit-llvm/parse_expr_arith.calc
rename to tests/lit-tests/parse_expr_arith.calc
index 940f545..993401a 100644
--- a/tests/lit-llvm/parse_expr_arith.calc
+++ b/tests/lit-tests/parse_expr_arith.calc
@@ -1,9 +1,9 @@
-// RUN: @calcc --verbose --ast --parse -e "32 + 50" 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc --verbose --ast --parse -e "32 - 50" 2>&1 | @filecheck %s --check-prefix=CHECK_B
-// RUN: @calcc --verbose --ast --parse -e "32 / 50" 2>&1 | @filecheck %s --check-prefix=CHECK_C
-// RUN: @calcc --verbose --ast --parse -e "32 * 50" 2>&1 | @filecheck %s --check-prefix=CHECK_D
-// RUN: @calcc --verbose --ast --parse -e "32*2/4 + 50/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_E
-// RUN: @calcc --verbose --ast --parse -e "32*-2/4 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_F
+// RUN: @calcc --verbose -k -S --ast --parse -e "32 + 50" 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc --verbose -k -S --ast --parse -e "32 - 50" 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc --verbose -k -S --ast --parse -e "32 / 50" 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc --verbose -k -S --ast --parse -e "32 * 50" 2>&1 | @filecheck %s --check-prefix=CHECK_D
+// RUN: @calcc --verbose -k -S --ast --parse -e "32*2/4 + 50/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_E
+// RUN: @calcc --verbose -k -S --ast --parse -e "32*-2/4 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_F
// CHECK_A: Consumed expected token 'Number' at position '0'
// CHECK_A: Consumed expected token 'Plus' at position '1'
diff --git a/tests/lit-llvm/parse_expr_unary_minus.calc b/tests/lit-tests/parse_expr_unary_minus.calc
similarity index 76%
rename from tests/lit-llvm/parse_expr_unary_minus.calc
rename to tests/lit-tests/parse_expr_unary_minus.calc
index ecfeabd..0a1b162 100644
--- a/tests/lit-llvm/parse_expr_unary_minus.calc
+++ b/tests/lit-tests/parse_expr_unary_minus.calc
@@ -1,10 +1,10 @@
-// RUN: @calcc --verbose --ast --parse -e "0 - N" 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc --verbose --ast --parse -e "-N" 2>&1 | @filecheck %s --check-prefix=CHECK_B
-// RUN: @calcc --verbose --ast --parse -e "-N + 2" 2>&1 | @filecheck %s --check-prefix=CHECK_C
-// RUN: @calcc --verbose --ast --parse -e "2 + -N" 2>&1 | @filecheck %s --check-prefix=CHECK_D
-// RUN: @calcc --verbose --ast --parse -e "-(N) + 2" 2>&1 | @filecheck %s --check-prefix=CHECK_E
-// RUN: @calcc --verbose --ast --parse -e "2 + -(N)" 2>&1 | @filecheck %s --check-prefix=CHECK_F
-// RUN: @calcc --verbose --ast --parse -e "2 + -(N*2)" 2>&1 | @filecheck %s --check-prefix=CHECK_G
+// RUN: @calcc --verbose -k -S --ast --parse -e "0 - N" 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc --verbose -k -S --ast --parse -e "-N" 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc --verbose -k -S --ast --parse -e "-N + 2" 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc --verbose -k -S --ast --parse -e "2 + -N" 2>&1 | @filecheck %s --check-prefix=CHECK_D
+// RUN: @calcc --verbose -k -S --ast --parse -e "-(N) + 2" 2>&1 | @filecheck %s --check-prefix=CHECK_E
+// RUN: @calcc --verbose -k -S --ast --parse -e "2 + -(N)" 2>&1 | @filecheck %s --check-prefix=CHECK_F
+// RUN: @calcc --verbose -k -S --ast --parse -e "2 + -(N*2)" 2>&1 | @filecheck %s --check-prefix=CHECK_G
// CHECK_A: Consumed expected token 'Number' at position '0'
// CHECK_A: Consumed expected token 'Minus' at position '1'
diff --git a/tests/lit-llvm/parse_expr_withdecl.calc b/tests/lit-tests/parse_expr_withdecl.calc
similarity index 89%
rename from tests/lit-llvm/parse_expr_withdecl.calc
rename to tests/lit-tests/parse_expr_withdecl.calc
index e149194..8df6723 100644
--- a/tests/lit-llvm/parse_expr_withdecl.calc
+++ b/tests/lit-tests/parse_expr_withdecl.calc
@@ -1,5 +1,5 @@
-// RUN: @calcc --verbose --ast --parse -e "with: a: 32*a/4 + a/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc --verbose --ast --parse -e "with: a,b: 32*a/b + a/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc --verbose -k -S --ast --parse -e "with: a: 32*a/4 + a/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc --verbose -k -S --ast --parse -e "with: a,b: 32*a/b + a/10 - 1" 2>&1 | @filecheck %s --check-prefix=CHECK_B
// CHECK_A: Consumed expected token 'With' at position '0'
// CHECK_A: Consumed expected token 'Colon' at position '1'
diff --git a/tests/lit-tests/parse_idents.calc b/tests/lit-tests/parse_idents.calc
new file mode 100644
index 0000000..fdae538
--- /dev/null
+++ b/tests/lit-tests/parse_idents.calc
@@ -0,0 +1,20 @@
+// RUN: @calcc --verbose -k -S --ast --parse -e a 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc --verbose -k -S --ast --parse -e point 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc --verbose -k -S --ast --parse -e Zpos 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc --verbose -k -S --ast --parse -e __file__ 2>&1 | @filecheck %s --check-prefix=CHECK_D
+// RUN: @calcc --verbose -k -S --ast --parse -e tmp002 2>&1 | @filecheck %s --check-prefix=CHECK_E
+
+// CHECK_A: Consumed expected token 'Ident' at position '0'
+// CHECK_A: AST: Ident(a)
+
+// CHECK_B: Consumed expected token 'Ident' at position '0'
+// CHECK_B: AST: Ident(point)
+
+// CHECK_C: Consumed expected token 'Ident' at position '0'
+// CHECK_C: AST: Ident(Zpos)
+
+// CHECK_D: Consumed expected token 'Ident' at position '0'
+// CHECK_D: AST: Ident(__file__)
+
+// CHECK_E: Consumed expected token 'Ident' at position '0'
+// CHECK_E: AST: Ident(tmp002)
diff --git a/tests/lit-tests/parse_numbers.calc b/tests/lit-tests/parse_numbers.calc
new file mode 100644
index 0000000..1ee3fc9
--- /dev/null
+++ b/tests/lit-tests/parse_numbers.calc
@@ -0,0 +1,31 @@
+// RUN: @calcc --verbose -k -S --ast --parse -e 256 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc --verbose -k -S --ast --parse -e 9 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: @calcc --verbose -k -S --ast --parse -e -9 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc --verbose -k -S --ast --parse -e "- 9" 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: @calcc --verbose -k -S --ast --parse -e "0x7FFFFFFFFFFFFFFF" 2>&1 | @filecheck %s --check-prefix=CHECK_D
+
+// RUN: not @calcc --verbose -k -S --parse -e "0x8000000000000000" 2>&1 | @filecheck %s --check-prefix=CHECK_E
+// RUN: not @calcc --verbose -k -S --parse -e "9223372036854775808" 2>&1 | @filecheck %s --check-prefix=CHECK_F
+// RUN: not @calcc --verbose -k -S --parse -e "-9223372036854775809" 2>&1 | @filecheck %s --check-prefix=CHECK_G
+
+// CHECK_A: Consumed expected token 'Number' at position '0'
+// CHECK_A: AST: 256
+
+// CHECK_B: Consumed expected token 'Number' at position '0'
+// CHECK_B: AST: 9
+
+// CHECK_C: Consumed expected token 'Minus' at position '0'
+// CHECK_C: Consumed expected token 'Number' at position '1'
+// CHECK_C: AST: -9
+
+// CHECK_D: Consumed expected token 'Number' at position '0'
+// CHECK_D: AST: 9223372036854775807
+
+// CHECK_E: Number '0x8000000000000000' failed parse: number too large to fit in target type
+// CHECK_E: Failed to convert hexadecimal string
+
+// CHECK_F: Number '9223372036854775808' failed parse: number too large to fit in target type
+// CHECK_F: Failed to convert decimal string
+
+// CHECK_G: Number '-9223372036854775809' failed parse: number too small to fit in target type
+// CHECK_G: Failed to convert decimal string
diff --git a/tests/lit-llvm/sem_decl.calc b/tests/lit-tests/sem_decl.calc
similarity index 56%
rename from tests/lit-llvm/sem_decl.calc
rename to tests/lit-tests/sem_decl.calc
index 82406d3..72b0f23 100644
--- a/tests/lit-llvm/sem_decl.calc
+++ b/tests/lit-tests/sem_decl.calc
@@ -1,8 +1,8 @@
-// RUN: @calcc --verbose --sem -e "with: a,b: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_A
-// RUN: @calcc --verbose --sem -e "with: a,b: b + 2*a" 2>&1 | @filecheck %s --check-prefix=CHECK_B
-// RUN: not @calcc --verbose --sem -e "with: a: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_C
-// RUN: not @calcc --verbose --sem -e "with: b: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_D
-// RUN: not @calcc --verbose --sem -e "with: b,b: 2*b + b" 2>&1 | @filecheck %s --check-prefix=CHECK_E
+// RUN: @calcc --verbose -k -S --sem -e "with: a,b: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_A
+// RUN: @calcc --verbose -k -S --sem -e "with: a,b: b + 2*a" 2>&1 | @filecheck %s --check-prefix=CHECK_B
+// RUN: not @calcc --verbose -k -S --sem -e "with: a: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_C
+// RUN: not @calcc --verbose -k -S --sem -e "with: b: 2*a + b" 2>&1 | @filecheck %s --check-prefix=CHECK_D
+// RUN: not @calcc --verbose -k -S --sem -e "with: b,b: 2*b + b" 2>&1 | @filecheck %s --check-prefix=CHECK_E
// CHECK_A: Added var 'a' to scope
// CHECK_A: Added var 'b' to scope
diff --git a/tests/lit-llvm/usage.calc b/tests/lit-tests/usage.calc
similarity index 100%
rename from tests/lit-llvm/usage.calc
rename to tests/lit-tests/usage.calc
diff --git a/tests/lit-llvm/version.calc b/tests/lit-tests/version.calc
similarity index 75%
rename from tests/lit-llvm/version.calc
rename to tests/lit-tests/version.calc
index b831d89..255ee6e 100644
--- a/tests/lit-llvm/version.calc
+++ b/tests/lit-tests/version.calc
@@ -1,5 +1,5 @@
// RUN: @calcc --version 2>&1 | @filecheck %s
-// CHECK: Welcome to calcc version v0.1.0-dev
+// CHECK: Welcome to calcc version v0.2.0
// CHECK: // Copyright 2024, Giordano Salvador
// CHECK: // SPDX-License-Identifier: BSD-3-Clause