Skip to main content

online_dsl_forge/sema/
verified.rs

1use std::collections::{BTreeMap, BTreeSet};
2
3use crate::parser::{AstExpression, BinaryOp, SourceSpan, UnaryOp};
4use regex::{Regex, RegexBuilder};
5
6use crate::sema::profile::{BodyNeedSummary, SecurityProfile};
7use crate::sema::schema::{CapabilityMeta, CapabilityTicket, RegexFlavor};
8
9#[derive(Debug, Clone)]
10pub struct VerifiedProgram {
11  ast: AstExpression,
12  root: VerifiedExpression,
13  profile: SecurityProfile,
14  body_need: BodyNeedSummary,
15  static_cost_upper_bound: u64,
16  regex_literals: Vec<RegexLiteral>,
17  regex_cache: CompiledRegexCache,
18  required_capabilities: BTreeSet<CapabilityTicket>,
19  required_capability_metadata: BTreeMap<CapabilityTicket, CapabilityMeta>,
20}
21
22impl VerifiedProgram {
23  pub(crate) fn new(parts: VerifiedProgramParts) -> Self {
24    Self {
25      ast: parts.ast,
26      root: parts.root,
27      profile: parts.profile,
28      body_need: parts.body_need,
29      static_cost_upper_bound: parts.static_cost_upper_bound,
30      regex_literals: parts.regex_literals,
31      regex_cache: parts.regex_cache,
32      required_capabilities: parts.required_capabilities,
33      required_capability_metadata: parts.required_capability_metadata,
34    }
35  }
36
37  pub fn ast(&self) -> &AstExpression {
38    &self.ast
39  }
40
41  pub fn root(&self) -> &VerifiedExpression {
42    &self.root
43  }
44
45  pub fn profile(&self) -> &SecurityProfile {
46    &self.profile
47  }
48
49  pub fn body_need(&self) -> BodyNeedSummary {
50    self.body_need
51  }
52
53  pub fn static_cost_upper_bound(&self) -> u64 {
54    self.static_cost_upper_bound
55  }
56
57  pub fn regex_literals(&self) -> &[RegexLiteral] {
58    &self.regex_literals
59  }
60
61  pub fn regex_cache(&self) -> &CompiledRegexCache {
62    &self.regex_cache
63  }
64
65  pub fn required_capabilities(&self) -> &BTreeSet<CapabilityTicket> {
66    &self.required_capabilities
67  }
68
69  pub fn required_capability_metadata(&self) -> &BTreeMap<CapabilityTicket, CapabilityMeta> {
70    &self.required_capability_metadata
71  }
72}
73
74pub(crate) struct VerifiedProgramParts {
75  pub ast: AstExpression,
76  pub root: VerifiedExpression,
77  pub profile: SecurityProfile,
78  pub body_need: BodyNeedSummary,
79  pub static_cost_upper_bound: u64,
80  pub regex_literals: Vec<RegexLiteral>,
81  pub regex_cache: CompiledRegexCache,
82  pub required_capabilities: BTreeSet<CapabilityTicket>,
83  pub required_capability_metadata: BTreeMap<CapabilityTicket, CapabilityMeta>,
84}
85
86#[derive(Debug, Clone)]
87pub struct CompiledExpression {
88  verified: VerifiedProgram,
89}
90
91impl CompiledExpression {
92  pub(crate) fn new(verified: VerifiedProgram) -> Self {
93    Self { verified }
94  }
95
96  pub fn ast(&self) -> &AstExpression {
97    self.verified.ast()
98  }
99
100  pub fn into_ast(self) -> AstExpression {
101    self.verified.ast
102  }
103
104  pub fn verified_program(&self) -> &VerifiedProgram {
105    &self.verified
106  }
107
108  pub fn into_verified_program(self) -> VerifiedProgram {
109    self.verified
110  }
111}
112
113#[derive(Debug, Clone)]
114pub struct VerifiedExpression {
115  kind: VerifiedExprKind,
116  span: SourceSpan,
117  capability_ticket: Option<CapabilityTicket>,
118}
119
120impl VerifiedExpression {
121  pub(crate) fn new(kind: VerifiedExprKind, span: SourceSpan) -> Self {
122    Self {
123      kind,
124      span,
125      capability_ticket: None,
126    }
127  }
128
129  pub(crate) fn with_capability_ticket(mut self, ticket: CapabilityTicket) -> Self {
130    self.capability_ticket = Some(ticket);
131    self
132  }
133
134  pub fn span(&self) -> SourceSpan {
135    self.span
136  }
137
138  pub fn capability_ticket(&self) -> Option<&CapabilityTicket> {
139    self.capability_ticket.as_ref()
140  }
141
142  pub fn kind(&self) -> VerifiedExprKindRef<'_> {
143    match &self.kind {
144      VerifiedExprKind::Null => VerifiedExprKindRef::Null,
145      VerifiedExprKind::Bool(value) => VerifiedExprKindRef::Bool(*value),
146      VerifiedExprKind::Int(value) => VerifiedExprKindRef::Int(*value),
147      VerifiedExprKind::Float(value) => VerifiedExprKindRef::Float(*value),
148      VerifiedExprKind::String(value) => VerifiedExprKindRef::String(value),
149      VerifiedExprKind::Array(items) => VerifiedExprKindRef::Array(items),
150      VerifiedExprKind::Identifier(name) => VerifiedExprKindRef::Identifier(name),
151      VerifiedExprKind::Member { receiver, name } => VerifiedExprKindRef::Member { receiver, name },
152      VerifiedExprKind::FunctionCall { name, args } => {
153        VerifiedExprKindRef::FunctionCall { name, args }
154      }
155      VerifiedExprKind::ExpressionFunctionCall {
156        name,
157        params,
158        args,
159        body,
160      } => VerifiedExprKindRef::ExpressionFunctionCall {
161        name,
162        params,
163        args,
164        body,
165      },
166      VerifiedExprKind::MethodCall {
167        receiver,
168        name,
169        args,
170      } => VerifiedExprKindRef::MethodCall {
171        receiver,
172        name,
173        args,
174      },
175      VerifiedExprKind::Unary { op, expr } => VerifiedExprKindRef::Unary { op: *op, expr },
176      VerifiedExprKind::Binary { left, op, right } => VerifiedExprKindRef::Binary {
177        left,
178        op: *op,
179        right,
180      },
181    }
182  }
183}
184
185#[derive(Debug, Clone)]
186pub(crate) enum VerifiedExprKind {
187  Null,
188  Bool(bool),
189  Int(i64),
190  Float(f64),
191  String(String),
192  Array(Vec<VerifiedExpression>),
193  Identifier(String),
194  Member {
195    receiver: Box<VerifiedExpression>,
196    name: String,
197  },
198  FunctionCall {
199    name: String,
200    args: Vec<VerifiedExpression>,
201  },
202  ExpressionFunctionCall {
203    name: String,
204    params: Vec<String>,
205    args: Vec<VerifiedExpression>,
206    body: Box<VerifiedExpression>,
207  },
208  MethodCall {
209    receiver: Box<VerifiedExpression>,
210    name: String,
211    args: Vec<VerifiedExpression>,
212  },
213  Unary {
214    op: UnaryOp,
215    expr: Box<VerifiedExpression>,
216  },
217  Binary {
218    left: Box<VerifiedExpression>,
219    op: BinaryOp,
220    right: Box<VerifiedExpression>,
221  },
222}
223
224pub enum VerifiedExprKindRef<'a> {
225  Null,
226  Bool(bool),
227  Int(i64),
228  Float(f64),
229  String(&'a str),
230  Array(&'a [VerifiedExpression]),
231  Identifier(&'a str),
232  Member {
233    receiver: &'a VerifiedExpression,
234    name: &'a str,
235  },
236  FunctionCall {
237    name: &'a str,
238    args: &'a [VerifiedExpression],
239  },
240  ExpressionFunctionCall {
241    name: &'a str,
242    params: &'a [String],
243    args: &'a [VerifiedExpression],
244    body: &'a VerifiedExpression,
245  },
246  MethodCall {
247    receiver: &'a VerifiedExpression,
248    name: &'a str,
249    args: &'a [VerifiedExpression],
250  },
251  Unary {
252    op: UnaryOp,
253    expr: &'a VerifiedExpression,
254  },
255  Binary {
256    left: &'a VerifiedExpression,
257    op: BinaryOp,
258    right: &'a VerifiedExpression,
259  },
260}
261
262#[derive(Debug, Clone, Eq, PartialEq)]
263pub struct RegexLiteral {
264  pub pattern: String,
265  pub flavor: RegexFlavor,
266  pub span: SourceSpan,
267}
268
269#[derive(Debug, Clone, Default)]
270pub struct CompiledRegexCache {
271  default: BTreeMap<String, Regex>,
272  header_name: BTreeMap<String, Regex>,
273}
274
275impl CompiledRegexCache {
276  pub fn insert(&mut self, literal: &RegexLiteral) -> Result<(), regex::Error> {
277    let target = match literal.flavor {
278      RegexFlavor::Default => &mut self.default,
279      RegexFlavor::HeaderName => &mut self.header_name,
280    };
281    if target.contains_key(&literal.pattern) {
282      return Ok(());
283    }
284    target.insert(literal.pattern.clone(), compile_regex(literal)?);
285    Ok(())
286  }
287
288  pub fn get(&self, flavor: RegexFlavor, pattern: &str) -> Option<&Regex> {
289    match flavor {
290      RegexFlavor::Default => self.default.get(pattern),
291      RegexFlavor::HeaderName => self.header_name.get(pattern),
292    }
293  }
294
295  pub fn is_match(&self, flavor: RegexFlavor, pattern: &str, haystack: &str) -> Option<bool> {
296    self
297      .get(flavor, pattern)
298      .map(|regex| regex.is_match(haystack))
299  }
300
301  pub fn len(&self) -> usize {
302    self.default.len() + self.header_name.len()
303  }
304
305  pub fn is_empty(&self) -> bool {
306    self.len() == 0
307  }
308}
309
310fn compile_regex(literal: &RegexLiteral) -> Result<Regex, regex::Error> {
311  match literal.flavor {
312    RegexFlavor::Default => Regex::new(&literal.pattern),
313    RegexFlavor::HeaderName => RegexBuilder::new(&literal.pattern)
314      .case_insensitive(true)
315      .build(),
316  }
317}