#![allow(warnings, clippy, unknown_lints)]
use std::{collections::BTreeMap, fmt::Display, io::Result, path::PathBuf, process::exit};
pub type Identifier = String;
pub type StringLiteral = String;
pub mod asm;
pub mod hir;
pub mod mir;
pub mod tir;
use hir::HirProgram;
use tir::TirProgram;
mod target;
pub use target::{Go, Target, C, TS};
use asciicolor::Colorize;
use comment::cpp::strip;
use lalrpop_util::{lalrpop_mod, ParseError};
lalrpop_mod!(pub parser);
pub fn generate_docs(cwd: &PathBuf, input: impl ToString, filename: impl ToString, target: impl Target) -> String {
match parse(input).compile(cwd) {
Ok(output) => output,
Err(e) => print_compile_error(e)
}.generate_docs(filename.to_string(), &target, &mut BTreeMap::new(), false)
}
fn print_compile_error(e: impl Display) -> ! {
eprintln!("compilation error: {}", e.bright_red().underline());
exit(1);
}
pub fn compile(cwd: &PathBuf, input: impl ToString, target: impl Target) -> Result<()> {
let mut tir = parse(input);
let mut hir = match tir.compile(cwd) {
Ok(output) => output,
Err(e) => print_compile_error(e)
};
hir.extend_declarations(match parse(include_str!("core.ok")).compile(cwd) {
Ok(output) => output,
Err(e) => print_compile_error(e)
}.get_declarations());
if hir.use_std() {
hir.extend_declarations(match parse(include_str!("std.ok")).compile(cwd) {
Ok(output) => output,
Err(e) => print_compile_error(e)
}.get_declarations());
}
match hir.compile(cwd, &target, &mut BTreeMap::new()) {
Ok(mir) => match mir.assemble() {
Ok(asm) => match asm.assemble(&target) {
Ok(result) => target.compile(if hir.use_std() {
target.core_prelude() + &target.std() + &result + &target.core_postlude()
} else {
target.core_prelude() + &result + &target.core_postlude()
}),
Err(e) => print_compile_error(e),
},
Err(e) => print_compile_error(e),
},
Err(e) => print_compile_error(e),
}
}
pub fn parse(input: impl ToString) -> TirProgram {
let code = &strip(input.to_string()).unwrap();
match parser::ProgramParser::new().parse(code) {
Ok(parsed) => parsed,
Err(e) => {
eprintln!("{}", format_error(&code, e));
exit(1);
}
}
}
type Error<'a, T> = ParseError<usize, T, &'a str>;
fn make_error(line: &str, unexpected: &str, line_number: usize, column_number: usize) -> String {
let underline = format!(
"{}^{}",
" ".repeat(column_number),
"-".repeat(unexpected.len() - 1)
);
format!(
"{WS} |
{line_number} | {line}
{WS} | {underline}
{WS} |
{WS} = unexpected `{unexpected}`",
WS = " ".repeat(line_number.to_string().len()),
line_number = line_number,
line = line.bright_yellow().underline(),
underline = underline,
unexpected = unexpected.bright_yellow().underline()
)
}
fn get_line(script: &str, location: usize) -> (usize, String, usize) {
let line_number = script[..location + 1].lines().count();
let line = match script.lines().nth(line_number - 1) {
Some(line) => line,
None => {
if let Some(line) = script.lines().last() {
line
} else {
""
}
}
}
.replace("\t", " ");
let mut column = {
let mut current_column = 0;
for ch in script[..location].chars() {
if ch == '\n' {
current_column = 0;
} else if ch == '\t' {
current_column += 4;
} else {
current_column += 1;
}
}
current_column
};
let trimmed_line = line.trim_start();
column -= (line.len() - trimmed_line.len()) as i32;
(line_number, String::from(trimmed_line), column as usize)
}
fn format_error<T: core::fmt::Debug>(script: &str, err: Error<T>) -> String {
match err {
Error::InvalidToken { location } => {
let (line_number, line, column) = get_line(script, location);
make_error(
&line,
&(script.as_bytes()[location] as char).to_string(),
line_number,
column,
)
}
Error::UnrecognizedEOF { location, .. } => {
let (line_number, line, _) = get_line(script, location);
make_error(&line, "EOF", line_number, line.len())
}
Error::UnrecognizedToken { token, .. } => {
let start = token.0;
let end = token.2;
let (line_number, line, column) = get_line(script, start);
let unexpected = &script[start..end];
make_error(&line, unexpected, line_number, column)
}
Error::ExtraToken { token } => {
let start = token.0;
let end = token.2;
let (line_number, line, column) = get_line(script, start);
let unexpected = &script[start..end];
make_error(&line, unexpected, line_number, column)
}
Error::User { error } => format!(
" |\n? | {}\n | {}\n |\n = unexpected compiling error",
error,
format!("^{}", "-".repeat(error.len() - 1))
),
}
}