1use std::fmt;
4
5mod interlang;
6
7pub use lace_sans_io::logic::InterlaceModule;
8use lace_sans_io::logic::prepare_facet_constraints;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum CompilationErrorKind {
12 Policy,
13 LimitExceeded,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct CompilationError {
18 pub kind: CompilationErrorKind,
19 pub message: String,
20}
21impl CompilationError {
22 pub fn is_limit_exceeded(&self) -> bool {
23 self.kind == CompilationErrorKind::LimitExceeded
24 }
25 pub fn limit_exceeded(message: impl Into<String>) -> Self {
26 Self {
27 kind: CompilationErrorKind::LimitExceeded,
28 message: message.into(),
29 }
30 }
31}
32impl From<lace_sans_io::logic::PreparationError> for CompilationError {
33 fn from(error: lace_sans_io::logic::PreparationError) -> Self {
34 Self {
35 kind: if error.is_limit_exceeded() {
36 CompilationErrorKind::LimitExceeded
37 } else {
38 CompilationErrorKind::Policy
39 },
40 message: error.to_string(),
41 }
42 }
43}
44impl From<String> for CompilationError {
45 fn from(value: String) -> Self {
46 Self {
47 kind: CompilationErrorKind::Policy,
48 message: value,
49 }
50 }
51}
52impl From<&str> for CompilationError {
53 fn from(value: &str) -> Self {
54 Self::from(value.to_owned())
55 }
56}
57impl fmt::Display for CompilationError {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 self.message.fmt(f)
60 }
61}
62impl std::error::Error for CompilationError {}
63impl From<CompilationError> for lace_sans_io::runtime_values::Error {
64 fn from(error: CompilationError) -> Self {
65 let category = if error.is_limit_exceeded() {
66 lace_sans_io::core::ErrorCategory::LimitExceeded
67 } else {
68 lace_sans_io::core::ErrorCategory::Policy
69 };
70 Self::detailed(category, None, None, error.message)
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct CompilationLimits {
76 pub max_source_bytes: usize,
78}
79impl Default for CompilationLimits {
80 fn default() -> Self {
81 Self {
82 max_source_bytes: 1 << 20,
83 }
84 }
85}
86
87pub fn compile(source: &str) -> Result<InterlaceModule, CompilationError> {
90 compile_with_limits(source, CompilationLimits::default())
91}
92
93pub fn compile_with_limits(
97 source: &str,
98 limits: CompilationLimits,
99) -> Result<InterlaceModule, CompilationError> {
100 if source.len() > limits.max_source_bytes {
101 return Err(CompilationError::limit_exceeded(
102 "source byte limit exceeded",
103 ));
104 }
105 let canonical = interlang::compile_source(source).map_err(|error| match error {
106 interlang::InterlangError::Parse(error) => {
107 let location = error.line_col(source);
108 CompilationError::from(format!(
109 "{}:{}: {}",
110 location.line, location.column, error.message
111 ))
112 }
113 interlang::InterlangError::Compile(diagnostics) => {
114 let kind = if diagnostics
115 .iter()
116 .any(|d| d.kind == interlang::CompileDiagnosticKind::LimitExceeded)
117 {
118 CompilationErrorKind::LimitExceeded
119 } else {
120 CompilationErrorKind::Policy
121 };
122 let message = diagnostics
123 .iter()
124 .map(|diagnostic| {
125 let location = diagnostic.line_col(source);
126 format!(
127 "{}:{}: {}",
128 location.line, location.column, diagnostic.message
129 )
130 })
131 .collect::<Vec<_>>()
132 .join("\n");
133 CompilationError { kind, message }
134 }
135 })?;
136 let module =
137 InterlaceModule::from_rules(canonical.program.strata.into_iter().flatten().collect())?;
138 Ok(prepare_facet_constraints(&[module], &[], &[])?)
139}