circom_lsp_parser/
lib.rs

1extern crate num_bigint_dig as num_bigint;
2extern crate num_traits;
3extern crate serde;
4extern crate serde_derive;
5#[macro_use]
6extern crate lalrpop_util;
7
8lalrpop_mod!(pub lang);
9
10mod include_logic;
11mod parser_logic;
12mod syntax_sugar_remover;
13
14use include_logic::{FileStack, IncludesGraph};
15use program_structure::ast::{produce_compiler_version_report, produce_report, produce_report_with_message, produce_version_warning_report, Expression};
16use program_structure::error_code::ReportCode;
17use program_structure::error_definition::ReportCollection;
18use program_structure::error_definition::Report;
19use program_structure::file_definition::{FileLibrary};
20use program_structure::program_archive::ProgramArchive;
21use std::path::{PathBuf, Path};
22use syntax_sugar_remover::{apply_syntactic_sugar};
23
24use std::str::FromStr;
25
26pub type Version = (usize, usize, usize);
27
28pub fn find_file(
29    crr_file: PathBuf,
30    ext_link_libraries: Vec<PathBuf>,
31) -> (bool, String, String, PathBuf, Vec<Report>) {
32    let mut found = false;
33    let mut path = "".to_string();
34    let mut src = "".to_string();
35    let mut crr_str_file = crr_file.clone();
36    let mut reports = Vec::new();
37    let mut i = 0;
38    while !found && i < ext_link_libraries.len() {
39        let mut p = PathBuf::new();
40        let aux = ext_link_libraries.get(i).unwrap();
41        p.push(aux);
42        p.push(crr_file.clone());
43        crr_str_file = p;
44        match open_file(crr_str_file.clone()) {
45            Ok((new_path, new_src)) => {
46                path = new_path;
47                src = new_src;
48                found = true;
49            }
50            Err(e) => {
51                reports.push(e);
52                i = i + 1;
53            }
54        }
55    }
56    (found, path, src, crr_str_file, reports)
57}
58
59pub fn run_parser(
60    file: String,
61    version: &str,
62    link_libraries: Vec<PathBuf>,
63) -> Result<(ProgramArchive, ReportCollection), (FileLibrary, ReportCollection)> {
64    let mut file_library = FileLibrary::new();
65    let mut definitions = Vec::new();
66    let mut main_components = Vec::new();
67    let mut file_stack = FileStack::new(PathBuf::from(file));
68    let mut includes_graph = IncludesGraph::new();
69    let mut warnings = Vec::new();
70    let mut link_libraries2 = link_libraries.clone();
71    let mut ext_link_libraries = vec![Path::new("").to_path_buf()];
72    ext_link_libraries.append(&mut link_libraries2);
73    while let Some(crr_file) = FileStack::take_next(&mut file_stack) {
74        let (found, path, src, crr_str_file, reports) =
75            find_file(crr_file, ext_link_libraries.clone());
76        if !found {
77            return Result::Err((file_library.clone(), reports));
78        }
79        let file_id = file_library.add_file(path.clone(), src.clone());
80        let program =
81            parser_logic::parse_file(&src, file_id).map_err(|e| (file_library.clone(), e))?;
82        if let Some(main) = program.main_component {
83            main_components.push((file_id, main, program.custom_gates));
84        }
85        includes_graph.add_node(crr_str_file, program.custom_gates, program.custom_gates_declared);
86        let includes = program.includes;
87        definitions.push((file_id, program.definitions));
88        for include in includes {
89            let path_include =
90                FileStack::add_include(&mut file_stack, include.clone(), &link_libraries.clone())
91                    .map_err(|e| (file_library.clone(), vec![e]))?;
92            includes_graph.add_edge(path_include).map_err(|e| (file_library.clone(), vec![e]))?;
93        }
94        warnings.append(
95            &mut check_number_version(
96                path.clone(),
97                program.compiler_version,
98                parse_number_version(version),
99            )
100            .map_err(|e| (file_library.clone(), vec![e]))?,
101        );
102        if program.custom_gates {
103            check_custom_gates_version(
104                path,
105                program.compiler_version,
106                parse_number_version(version),
107            )
108            .map_err(|e| (file_library.clone(), vec![e]))?
109        }
110    }
111
112    if main_components.len() == 0 {
113        let report = produce_report(ReportCode::NoMainFoundInProject,0..0, 0);
114        warnings.push(report);
115        Err((file_library, warnings))
116    } else if main_components.len() > 1 {
117        let report = produce_report_with_main_components(main_components);
118        warnings.push(report);
119        Err((file_library, warnings))
120    } else {
121        let mut errors: ReportCollection = includes_graph.get_problematic_paths().iter().map(|path|
122            Report::error(
123                format!(
124                    "Missing custom templates pragma in file {} because of the following chain of includes {}",
125                    path.last().unwrap().display(),
126                    IncludesGraph::display_path(path)
127                ),
128                ReportCode::CustomGatesPragmaError
129            )
130        ).collect();
131        if errors.len() > 0 {
132            warnings.append(& mut errors);
133            Err((file_library, warnings))
134        } else {
135            let (main_id, main_component, custom_gates) = main_components.pop().unwrap();
136            let result_program_archive = ProgramArchive::new(
137                file_library,
138                main_id,
139                main_component,
140                definitions,
141                custom_gates,
142            );
143            match result_program_archive {
144                Err((lib, mut rep)) => {
145                    warnings.append(&mut rep);
146                    Err((lib, warnings))
147                }
148                Ok(mut program_archive) => {
149                    let lib = program_archive.get_file_library().clone();
150                    let program_archive_result = apply_syntactic_sugar( &mut program_archive);
151                    match program_archive_result {
152                        Result::Err(v) => {
153                            warnings.push(v);
154                            Result::Err((lib,warnings))},
155                        Result::Ok(_) => Ok((program_archive, warnings)),
156                    }
157                }
158            }
159        }
160    }
161}
162
163fn produce_report_with_main_components(main_components: Vec<(usize, (Vec<String>, Expression), bool)>) -> Report {
164    let mut j = 0;
165    let mut r = produce_report(ReportCode::MultipleMain, 0..0, 0);
166    for (i,exp,_) in main_components{
167        if j > 0 {
168            r.add_secondary(exp.1.get_meta().location.clone(), i, Option::Some("Here it is another main component".to_string()));
169        }
170        else {
171            r.add_primary(exp.1.get_meta().location.clone(), i, "This is a main component".to_string());
172        }
173        j+=1;
174    }
175    r
176}
177
178fn open_file(path: PathBuf) -> Result<(String, String), Report> /* path, src */ {
179    use std::fs::read_to_string;
180    let path_str = format!("{:?}", path);
181    read_to_string(path)
182        .map(|contents| (path_str.clone(), contents))
183        .map_err(|_| produce_report_with_message(ReportCode::FileOs, path_str.clone()))
184}
185
186fn parse_number_version(version: &str) -> Version {
187    let version_splitted: Vec<&str> = version.split(".").collect();
188    (
189        usize::from_str(version_splitted[0]).unwrap(),
190        usize::from_str(version_splitted[1]).unwrap(),
191        usize::from_str(version_splitted[2]).unwrap(),
192    )
193}
194
195fn check_number_version(
196    file_path: String,
197    version_file: Option<Version>,
198    version_compiler: Version,
199) -> Result<ReportCollection, Report> {
200    if let Some(required_version) = version_file {
201        if required_version.0 == version_compiler.0
202            && (required_version.1 < version_compiler.1
203                || (required_version.1 == version_compiler.1
204                    && required_version.2 <= version_compiler.2))
205        {
206            Ok(vec![])
207        } else {
208            Err(produce_compiler_version_report(file_path, required_version, version_compiler))
209        }
210    } else {
211        let report =
212            produce_version_warning_report(file_path, version_compiler);
213        Ok(vec![report])
214    }
215}
216
217fn check_custom_gates_version(
218    file_path: String,
219    version_file: Option<Version>,
220    version_compiler: Version,
221) -> Result<(), Report> {
222    let custom_gates_version: Version = (2, 0, 6);
223    if let Some(required_version) = version_file {
224        if required_version.0 < custom_gates_version.0
225            || (required_version.0 == custom_gates_version.0
226                && required_version.1 < custom_gates_version.1)
227            || (required_version.0 == custom_gates_version.0
228                && required_version.1 == custom_gates_version.1
229                && required_version.2 < custom_gates_version.2)
230        {
231            let report = Report::error(
232                format!(
233                    "File {} requires at least version {:?} to use custom templates (currently {:?})",
234                    file_path,
235                    custom_gates_version,
236                    required_version
237                ),
238                ReportCode::CustomGatesVersionError
239            );
240            return Err(report);
241        }
242    } else {
243        if version_compiler.0 < custom_gates_version.0
244            || (version_compiler.0 == custom_gates_version.0
245                && version_compiler.1 < custom_gates_version.1)
246            || (version_compiler.0 == custom_gates_version.0
247                && version_compiler.1 == custom_gates_version.1
248                && version_compiler.2 < custom_gates_version.2)
249        {
250            let report = Report::error(
251                format!(
252                    "File {} does not include pragma version and the compiler version (currently {:?}) should be at least {:?} to use custom templates",
253                    file_path,
254                    version_compiler,
255                    custom_gates_version
256                ),
257                ReportCode::CustomGatesVersionError
258            );
259            return Err(report);
260        }
261    }
262    Ok(())
263}