slang/
lib.rs

1#![warn(clippy::todo)]
2
3//! # slang
4
5pub mod ast;
6mod cmd_ext;
7mod expr_ext;
8mod parse;
9pub mod smt;
10mod span;
11pub mod tc;
12
13use std::sync::{Arc, RwLock};
14
15use ast::{DomainRef, FunctionRef, MethodRef, Ref};
16use indexmap::IndexMap;
17pub use parse::ParseResult;
18pub use span::{Position, Span};
19
20/// A parsed and type checked source file.
21#[derive(Debug)]
22pub struct SourceFile {
23    items: Arc<Items>,
24    /// Errors that occurred during parsing.
25    pub parse_errors: Vec<parse::ParseError>,
26    /// Errors that occurred during type checking.
27    pub tc_errors: Vec<tc::Error>,
28}
29impl SourceFile {
30    /// Returns the methods defined in the source file.
31    pub fn methods(&self) -> Vec<Arc<ast::Method>> {
32        self.items.methods()
33    }
34    /// Returns the functions defined in the source file.
35    pub fn functions(&self) -> Vec<Arc<ast::Function>> {
36        self.items.functions()
37    }
38    /// Returns the domains defined in the source file.
39    pub fn domains(&self) -> Vec<Arc<ast::Domain>> {
40        self.items.domains()
41    }
42    /// Returns the globals defined in the source file.
43    pub fn globals(&self) -> Vec<Arc<ast::Global>> {
44        self.items.globals()
45    }
46
47    /// Returns the method with the given identifier.
48    ///
49    /// The returned ref will be unresolved if no method with that name exists.
50    pub fn get_method_ref(&self, ident: String) -> MethodRef {
51        MethodRef(self.items.new_ref(ident))
52    }
53    /// Returns the function with the given identifier.
54    ///
55    /// The returned ref will be unresolved if no function with that name
56    /// exists.
57    pub fn get_function_ref(&self, ident: String) -> FunctionRef {
58        FunctionRef(self.items.new_ref(ident))
59    }
60    /// Returns the domain with the given identifier.
61    ///
62    /// The returned ref will be unresolved if no domain with that name exists.
63    pub fn get_domain_ref(&self, ident: String) -> DomainRef {
64        DomainRef(self.items.new_ref(ident))
65    }
66
67    /// Returns true if there are any parse or type checking errors.
68    pub fn contains_error(&self) -> bool {
69        !self.parse_errors.is_empty() || !self.tc_errors.is_empty()
70    }
71}
72
73/// A collection of items defined in a source file.
74#[non_exhaustive]
75#[derive(Debug, Default)]
76pub struct Items {
77    methods: RwLock<IndexMap<String, Arc<ast::Method>>>,
78    functions: RwLock<IndexMap<String, (bool, Arc<ast::Function>)>>,
79    domains: RwLock<IndexMap<String, Arc<ast::Domain>>>,
80    globals: RwLock<IndexMap<String, Arc<ast::Global>>>,
81}
82
83impl Items {
84    fn new_ref(self: &Arc<Self>, ident: String) -> Ref {
85        Ref::Resolved(ident, Arc::downgrade(self))
86    }
87    /// Get the list of methods defined in the source file.
88    pub fn methods(&self) -> Vec<Arc<ast::Method>> {
89        self.methods.read().unwrap().values().cloned().collect()
90    }
91    /// Get the method with the given identifier, if any exists.
92    pub fn method(&self, id: &str) -> Option<Arc<ast::Method>> {
93        self.methods.read().ok()?.get(id).cloned()
94    }
95    fn insert_method(&self, id: String, ast: Arc<ast::Method>) {
96        self.methods.write().unwrap().insert(id, ast);
97    }
98    /// Get the list of functions defined in the source file.
99    pub fn functions(&self) -> Vec<Arc<ast::Function>> {
100        self.functions
101            .read()
102            .unwrap()
103            .values()
104            .filter_map(|(is_domain, f)| if *is_domain { None } else { Some(f.clone()) })
105            .collect()
106    }
107    /// Get the function with the given identifier, if any exists.
108    pub fn function(&self, id: &str) -> Option<Arc<ast::Function>> {
109        Some(self.functions.read().ok()?.get(id).cloned()?.1)
110    }
111    fn insert_function(&self, id: String, is_domain: bool, ast: Arc<ast::Function>) {
112        self.functions.write().unwrap().insert(id, (is_domain, ast));
113    }
114    /// Get the list of domains defined in the source file.
115    pub fn domains(&self) -> Vec<Arc<ast::Domain>> {
116        self.domains.read().unwrap().values().cloned().collect()
117    }
118    /// Get the domain with the given identifier, if any exists.
119    pub fn domain(&self, id: &str) -> Option<Arc<ast::Domain>> {
120        self.domains.read().ok()?.get(id).cloned()
121    }
122    fn insert_domain(&self, id: String, ast: Arc<ast::Domain>) {
123        self.domains.write().unwrap().insert(id, ast);
124    }
125    /// Get the list of globals defined in the source file.
126    pub fn globals(&self) -> Vec<Arc<ast::Global>> {
127        self.globals.read().unwrap().values().cloned().collect()
128    }
129    /// Get the global with the given identifier, if any exists.
130    pub fn global(&self, id: &str) -> Option<Arc<ast::Global>> {
131        self.globals.read().ok()?.get(id).cloned()
132    }
133    fn insert_global(&self, id: String, ast: Arc<ast::Global>) {
134        self.globals.write().unwrap().insert(id, ast);
135    }
136}
137
138pub fn parse_file(src: &str) -> SourceFile {
139    let ParseResult {
140        ast,
141        errors: parse_errors,
142    } = parse::file(src);
143
144    if let Some((items, tc_errors)) = ast.map(|file| file.tc()) {
145        SourceFile {
146            items,
147            parse_errors,
148            tc_errors,
149        }
150    } else {
151        SourceFile {
152            items: Default::default(),
153            parse_errors,
154            tc_errors: Vec::new(),
155        }
156    }
157}