lipstick_compiler/
lib.rs

1use std::{
2    fmt::{self, Debug, Display},
3    ops::Range,
4};
5
6use codespan_reporting::{
7    diagnostic::{Diagnostic, Label},
8    files::{Files, SimpleFile},
9    term,
10};
11use proc_macro2::{LineColumn, Span};
12
13mod c_lang;
14mod codegen;
15mod typecheck;
16
17/// A compilation session
18pub struct Session {
19    files: SimpleFile<String, String>,
20}
21
22impl Session {
23    /// Creates a compilation session from a source file
24    pub fn new(file_name: String, source: String) -> Self {
25        Self {
26            files: SimpleFile::new(file_name, source),
27        }
28    }
29
30    #[cfg(feature = "dev")]
31    pub fn debug(&self) -> Result<String, Box<dyn std::error::Error>> {
32        Ok(format!("{:#?}", syn::parse_file(&self.files.source())?))
33    }
34
35    /// Compiles the program into something ready to be printed as C code
36    pub fn compile(&self) -> Result<impl Display, SessionError> {
37        let syn_file = syn::parse_file(self.files.source()).map_err(|e| {
38            let error = CompilationError {
39                msg: format!("{}", e),
40                span: e.span(),
41            };
42
43            SessionError {
44                errors: vec![error],
45                syntax: true,
46                files: &self.files,
47            }
48        })?;
49
50        let type_info = typecheck::check(&syn_file).map_err(|e| SessionError {
51            errors: e.errors,
52            files: &self.files,
53            syntax: false,
54        })?;
55
56        match codegen::transform(&syn_file, type_info) {
57            Ok(program) => Ok(program),
58            Err(e) => Err(SessionError {
59                errors: e.errors,
60                syntax: false,
61                files: &self.files,
62            }),
63        }
64    }
65}
66
67/// An error during compilation
68pub struct SessionError<'a> {
69    /// Whether the error is due to parsing
70    pub syntax: bool,
71
72    /// the individual errors
73    pub errors: Vec<CompilationError>,
74
75    // diagnostics: Vec<Diagnostic<()>>,
76    files: &'a SimpleFile<String, String>,
77}
78
79impl<'a> SessionError<'a> {
80    /// Reports the error and exits the process
81    pub fn report(self) -> ! {
82        let mut writer =
83            term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Auto);
84
85        self.report_to_writer(&mut writer);
86
87        std::process::exit(1);
88    }
89
90    pub fn into_ansi(mut self) -> Vec<(String, Span)> {
91        let errors = {
92            let mut errors = vec![];
93
94            std::mem::swap(&mut errors, &mut self.errors);
95
96            errors
97        };
98
99        errors
100            .into_iter()
101            .map(|error| (error.span, self.map_err(error)))
102            .map(|(span, diagnostic)| {
103                let mut buffer = term::termcolor::Buffer::ansi();
104
105                term::emit(
106                    &mut buffer,
107                    &term::Config::default(),
108                    self.files,
109                    &diagnostic,
110                )
111                .unwrap();
112
113                (String::from_utf8(buffer.into_inner()).unwrap(), span)
114            })
115            .collect()
116    }
117
118    fn report_to_writer(
119        mut self,
120        writer: &mut dyn codespan_reporting::term::termcolor::WriteColor,
121    ) {
122        let n_of_errors = self.errors.len();
123
124        let errors = {
125            let mut errors = vec![];
126
127            std::mem::swap(&mut errors, &mut self.errors);
128
129            errors
130        };
131
132        for diagnostic in errors.into_iter().map(|err| self.map_err(err)) {
133            term::emit(writer, &term::Config::default(), self.files, &diagnostic).unwrap()
134        }
135
136        write!(writer, "Compilation failed due to {} errors", n_of_errors).unwrap();
137    }
138
139    fn map_err(&self, error: CompilationError) -> Diagnostic<()> {
140        Diagnostic::error()
141            .with_message(error.msg)
142            .with_labels(vec![Label::primary((), self.span_to_range(error.span))])
143    }
144
145    fn span_to_range(&self, span: Span) -> Range<usize> {
146        self.linecolumn_to_index(span.start())..self.linecolumn_to_index(span.end())
147    }
148
149    fn linecolumn_to_index(&self, linecol: LineColumn) -> usize {
150        let range = self.files.line_range((), linecol.line - 1).unwrap();
151
152        range.start + linecol.column
153    }
154}
155
156impl<'a> Display for SessionError<'a> {
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        std::writeln!(f, "Failed due to {} errors", self.errors.len())
159    }
160}
161
162impl<'a> Debug for SessionError<'a> {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        Display::fmt(self, f)
165    }
166}
167
168impl<'a> std::error::Error for SessionError<'a> {}
169
170/// A compilation error
171#[derive(Debug)]
172pub struct CompilationError {
173    pub span: Span,
174    pub msg: String,
175}
176
177#[derive(Debug)]
178struct CompilationErrors {
179    errors: Vec<CompilationError>,
180}
181
182impl Display for CompilationErrors {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        for error in &self.errors {
185            writeln!(f, "Error: {} ({:?})", error.msg, error.span)?;
186        }
187
188        Ok(())
189    }
190}
191
192impl std::error::Error for CompilationErrors {}