use crate::session::Session;
pub const M_NEW: &str = "\x01\x02c-shell/new\x02\x01";
pub const M_VAL: &str = "\x01\x02c-shell/val\x02\x01";
const HEADERS: &str = "\
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#include <ctype.h>
";
const RUNTIME: &str = r##"
#define CS_VAL() fputs(CS_M_VAL, stdout)
static inline void cs_p_b (_Bool v) { CS_VAL(); printf("%s\n", v ? "true" : "false"); }
static inline void cs_p_c (char v) { CS_VAL(); printf("'%c' (%d)\n", (v >= 32 && v < 127) ? v : '?', (int)v); }
static inline void cs_p_i (long long v) { CS_VAL(); printf("%lld\n", v); }
static inline void cs_p_u (unsigned long long v) { CS_VAL(); printf("%llu\n", v); }
static inline void cs_p_d (double v) { CS_VAL(); printf("%g\n", v); }
static inline void cs_p_ld(long double v) { CS_VAL(); printf("%Lg\n", v); }
static inline void cs_p_s (const char *v) { CS_VAL(); if (v) printf("\"%s\"\n", v); else printf("(null)\n"); }
static inline void cs_p_p (const void *v) { CS_VAL(); printf("%p\n", (void *)v); }
#define CS_PRINT(x) _Generic((x), \
_Bool: cs_p_b, char: cs_p_c, \
signed char: cs_p_i, unsigned char: cs_p_u, \
short: cs_p_i, unsigned short: cs_p_u, \
int: cs_p_i, unsigned int: cs_p_u, \
long: cs_p_i, unsigned long: cs_p_u, \
long long: cs_p_i, unsigned long long: cs_p_u, \
float: cs_p_d, double: cs_p_d, \
long double: cs_p_ld, \
char *: cs_p_s, const char *: cs_p_s, \
default: cs_p_p)(x)
#define CS_MARK(m) do { \
fputs(m, stdout); fflush(stdout); \
fputs(m, stderr); fflush(stderr); \
} while (0)
"##;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Slot {
Expr,
Stmt,
FileScope,
}
pub struct Program {
pub src: String,
pub new_start_line: usize,
pub new_line_count: usize,
pub wrapped: bool,
}
pub fn build(session: &Session, input: &str, slot: Slot) -> Program {
let mut src = String::with_capacity(4096);
let mut new_start_line = 1usize;
src.push_str(HEADERS);
src.push_str(&format!("#define CS_M_NEW \"{}\"\n", escape(M_NEW)));
src.push_str(&format!("#define CS_M_VAL \"{}\"\n", escape(M_VAL)));
src.push_str(RUNTIME);
for item in &session.file_items {
src.push_str(item);
src.push('\n');
}
if slot == Slot::FileScope {
new_start_line = src.lines().count() + 1;
src.push_str(input);
src.push('\n');
}
src.push_str("\nint main(void)\n{\n");
for stmt in &session.stmts {
src.push_str(stmt);
src.push('\n');
}
src.push_str(" CS_MARK(CS_M_NEW);\n");
match slot {
Slot::FileScope => {}
Slot::Stmt => {
new_start_line = src.lines().count() + 1;
src.push_str(input);
src.push('\n');
}
Slot::Expr => {
new_start_line = src.lines().count() + 2;
src.push_str(&format!(" CS_PRINT((\n{input}\n ));\n"));
}
}
src.push_str(" return 0;\n}\n");
let new_line_count = input.lines().count().max(1);
Program {
src,
new_start_line,
new_line_count,
wrapped: slot == Slot::Expr,
}
}
fn escape(s: &str) -> String {
s.bytes().map(|b| format!("\\x{b:02x}")).collect()
}