diff --git a/src/compiler/semantic/analyser.rs b/src/compiler/semantic/analyser.rs index 319ae91b..0e768cec 100644 --- a/src/compiler/semantic/analyser.rs +++ b/src/compiler/semantic/analyser.rs @@ -61,11 +61,7 @@ impl Analyser { output_params, block, } => { - let sym = SymTableEntry { - definition_ref: dsym, - category: SymbolCategory::Machine, - ty: None, - }; + let sym = SymTableEntry::new(id.name(), dsym, SymbolCategory::Machine, None); RULES.apply_new_symbol_tldecl(self, decl, &id, &sym); @@ -98,22 +94,24 @@ impl Analyser { fn analyse_machine_input_params(&mut self, params: Vec>) { params.iter().for_each(|param| match param { Statement::SignalDecl(dsym, ids) => ids.iter().for_each(|id| { - let sym = SymTableEntry { - definition_ref: dsym.clone(), - category: SymbolCategory::InputSignal, - ty: id.ty.clone().map(|ty| ty.name()), - }; + let sym = SymTableEntry::new( + id.id.name(), + dsym.clone(), + SymbolCategory::InputSignal, + id.ty.clone().map(|ty| ty.name()), + ); RULES.apply_new_symbol_statement(self, param, &id.id, &sym); self.symbols.add_symbol(&self.cur_scope, id.id.name(), sym); }), Statement::WGVarDecl(dsym, ids) => ids.iter().for_each(|id| { - let sym = SymTableEntry { - definition_ref: dsym.clone(), - category: SymbolCategory::InputWGVar, - ty: id.ty.clone().map(|ty| ty.name()), - }; + let sym = SymTableEntry::new( + id.id.name(), + dsym.clone(), + SymbolCategory::InputWGVar, + id.ty.clone().map(|ty| ty.name()), + ); RULES.apply_new_symbol_statement(self, param, &id.id, &sym); @@ -126,11 +124,12 @@ impl Analyser { fn analyse_machine_output_params(&mut self, params: Vec>) { params.iter().for_each(|param| match param { Statement::SignalDecl(dsym, ids) => ids.iter().for_each(|id| { - let sym = SymTableEntry { - definition_ref: dsym.clone(), - category: SymbolCategory::OutputSignal, - ty: id.ty.clone().map(|ty| ty.name()), - }; + let sym = SymTableEntry::new( + id.id.name(), + dsym.clone(), + SymbolCategory::OutputSignal, + id.ty.clone().map(|ty| ty.name()), + ); RULES.apply_new_symbol_statement(self, param, &id.id, &sym); @@ -138,11 +137,12 @@ impl Analyser { .add_output_variable(&self.cur_scope, id.id.name(), sym); }), Statement::WGVarDecl(dsym, ids) => ids.iter().for_each(|id| { - let sym = SymTableEntry { - definition_ref: dsym.clone(), - category: SymbolCategory::OutputWGVar, - ty: id.ty.clone().map(|ty| ty.name()), - }; + let sym = SymTableEntry::new( + id.id.name(), + dsym.clone(), + SymbolCategory::OutputWGVar, + id.ty.clone().map(|ty| ty.name()), + ); RULES.apply_new_symbol_statement(self, param, &id.id, &sym); @@ -161,11 +161,8 @@ impl Analyser { if let Statement::Block(_, stmts) = block { stmts.iter().for_each(|stmt| { if let Statement::StateDecl(dsym, id, _) = stmt { - let sym = SymTableEntry { - definition_ref: dsym.clone(), - category: SymbolCategory::State, - ty: None, - }; + let sym = + SymTableEntry::new(id.name(), dsym.clone(), SymbolCategory::State, None); RULES.apply_new_symbol_statement(self, stmt, id, &sym); @@ -181,18 +178,14 @@ impl Analyser { .get_symbol(&self.cur_scope, "final".to_string()) .is_none() { - let id = Identifier("final".to_string(), 0); + let id = Identifier("final".to_string(), 0, block.get_dsym()); let stmt = Statement::StateDecl( block.get_dsym(), id.clone(), Box::new(Statement::Block(block.get_dsym(), vec![])), ); - let sym = SymTableEntry { - definition_ref: block.get_dsym(), - category: SymbolCategory::State, - ty: None, - }; + let sym = SymTableEntry::new(id.name(), block.get_dsym(), SymbolCategory::State, None); RULES.apply_new_symbol_statement(self, &stmt, &id, &sym); @@ -219,22 +212,24 @@ impl Analyser { fn statement_add_symbols(&mut self, stmt: &Statement) { match stmt.clone() { Statement::SignalDecl(dsym, ids) => ids.into_iter().for_each(|id| { - let sym = SymTableEntry { - category: SymbolCategory::Signal, - definition_ref: dsym.clone(), - ty: id.ty.map(|ty| ty.name()), - }; + let sym = SymTableEntry::new( + id.id.name(), + dsym.clone(), + SymbolCategory::Signal, + id.ty.map(|ty| ty.name()), + ); RULES.apply_new_symbol_statement(self, stmt, &id.id, &sym); self.symbols.add_symbol(&self.cur_scope, id.id.name(), sym); }), Statement::WGVarDecl(dsym, ids) => ids.into_iter().for_each(|id| { - let sym = SymTableEntry { - category: SymbolCategory::WGVar, - definition_ref: dsym.clone(), - ty: id.ty.map(|ty| ty.name()), - }; + let sym = SymTableEntry::new( + id.id.name(), + dsym.clone(), + SymbolCategory::WGVar, + id.ty.map(|ty| ty.name()), + ); RULES.apply_new_symbol_statement(self, stmt, &id.id, &sym); @@ -250,12 +245,18 @@ impl Analyser { fn analyse_statement_recursive(&mut self, stmt: Statement) { match stmt { Statement::Assert(_, expr) => self.analyse_expression(expr), - Statement::SignalAssignment(_, _, exprs) => exprs - .into_iter() - .for_each(|expr| self.analyse_expression(expr)), - Statement::SignalAssignmentAssert(_, _, exprs) => exprs - .into_iter() - .for_each(|expr| self.analyse_expression(expr)), + Statement::SignalAssignment(_, ids, exprs) => { + exprs + .into_iter() + .for_each(|expr| self.analyse_expression(expr)); + self.collect_id_usages(&ids); + } + Statement::SignalAssignmentAssert(_, ids, exprs) => { + exprs + .into_iter() + .for_each(|expr| self.analyse_expression(expr)); + self.collect_id_usages(&ids); + } Statement::WGAssignment(_, _, exprs) => exprs .into_iter() .for_each(|expr| self.analyse_expression(expr)), @@ -270,7 +271,10 @@ impl Analyser { } Statement::StateDecl(_, id, block) => self.analyse_state(id, *block), - Statement::Transition(_, _, block) => self.analyse_statement(*block), + Statement::Transition(_, id, block) => { + self.analyse_statement(*block); + self.collect_id_usages(&[id]); + } Statement::Block(_, stmts) => stmts .into_iter() @@ -282,7 +286,40 @@ impl Analyser { } fn analyse_expression(&mut self, expr: Expression) { - RULES.apply_expression(self, &expr) + RULES.apply_expression(self, &expr); + self.extract_usages_expression(&expr); + } + + fn collect_id_usages(&mut self, ids: &[Identifier]) { + for id in ids { + self.symbols + .add_symbol_usage(&self.cur_scope, id.name(), id.debug_sym_ref()); + } + } + + fn extract_usages_expression(&mut self, expr: &Expression) { + match expr.clone() { + Expression::Query(_, id) => { + self.collect_id_usages(&[id]); + } + Expression::BinOp { + dsym: _, + op: _, + lhs, + rhs, + } => { + self.extract_usages_expression(&lhs); + self.extract_usages_expression(&rhs); + } + Expression::UnaryOp { + dsym: _, + op: _, + sub, + } => { + self.extract_usages_expression(&sub); + } + _ => {} + } } pub(super) fn error>(&mut self, msg: S, dsym: &DebugSymRef) { @@ -360,7 +397,7 @@ mod test { assert_eq!( format!("{:?}", result), - r#"AnalysisResult { symbols: "/": ScopeTable { symbols: "\"fibo\": SymTableEntry { definition_ref: DebugSymRef { start: \"2:9\", end: \"40:13\" }, category: Machine, ty: None }", scope: Global },"//fibo": ScopeTable { symbols: "\"a\": SymTableEntry { definition_ref: DebugSymRef { line: 5, cols: \"13-32\" }, category: Signal, ty: Some(\"field\") },\"b\": SymTableEntry { definition_ref: DebugSymRef { line: 2, cols: \"33-48\" }, category: OutputSignal, ty: Some(\"field\") },\"final\": SymTableEntry { definition_ref: DebugSymRef { start: \"2:50\", end: \"40:13\" }, category: State, ty: None },\"i\": SymTableEntry { definition_ref: DebugSymRef { line: 5, cols: \"13-32\" }, category: Signal, ty: None },\"initial\": SymTableEntry { definition_ref: DebugSymRef { start: \"10:13\", end: \"18:14\" }, category: State, ty: None },\"middle\": SymTableEntry { definition_ref: DebugSymRef { start: \"20:13\", end: \"34:14\" }, category: State, ty: None },\"n\": SymTableEntry { definition_ref: DebugSymRef { line: 2, cols: \"22-30\" }, category: InputSignal, ty: None }", scope: Machine },"//fibo/final": ScopeTable { symbols: "", scope: State },"//fibo/initial": ScopeTable { symbols: "\"c\": SymTableEntry { definition_ref: DebugSymRef { line: 11, cols: \"14-23\" }, category: Signal, ty: None }", scope: State },"//fibo/middle": ScopeTable { symbols: "\"c\": SymTableEntry { definition_ref: DebugSymRef { line: 21, cols: \"14-23\" }, category: Signal, ty: None }", scope: State }, messages: [] }"# + r#"AnalysisResult { symbols: "/": ScopeTable { symbols: "\"fibo\": SymTableEntry { id: \"fibo\", definition_ref: DebugSymRef { start: \"2:9\", end: \"40:13\" }, usages: [], category: Machine, ty: None }", scope: Global },"//fibo": ScopeTable { symbols: "\"a\": SymTableEntry { id: \"a\", definition_ref: DebugSymRef { line: 5, cols: \"13-32\" }, usages: [DebugSymRef { line: 13, cols: \"17-18\" }, DebugSymRef { line: 16, cols: \"15-17\" }, DebugSymRef { line: 23, cols: \"20-21\" }, DebugSymRef { line: 31, cols: \"20-22\" }], category: Signal, ty: Some(\"field\") },\"b\": SymTableEntry { id: \"b\", definition_ref: DebugSymRef { line: 2, cols: \"33-48\" }, usages: [DebugSymRef { line: 13, cols: \"20-21\" }, DebugSymRef { line: 16, cols: \"30-31\" }, DebugSymRef { line: 16, cols: \"19-21\" }, DebugSymRef { line: 23, cols: \"24-25\" }, DebugSymRef { line: 27, cols: \"20-22\" }, DebugSymRef { line: 31, cols: \"42-43\" }, DebugSymRef { line: 31, cols: \"24-26\" }], category: OutputSignal, ty: Some(\"field\") },\"final\": SymTableEntry { id: \"final\", definition_ref: DebugSymRef { start: \"2:50\", end: \"40:13\" }, usages: [DebugSymRef { line: 26, cols: \"18-23\" }], category: State, ty: None },\"i\": SymTableEntry { id: \"i\", definition_ref: DebugSymRef { line: 5, cols: \"13-32\" }, usages: [DebugSymRef { line: 13, cols: \"14-15\" }, DebugSymRef { line: 25, cols: \"17-18\" }, DebugSymRef { line: 27, cols: \"31-32\" }, DebugSymRef { line: 27, cols: \"16-18\" }, DebugSymRef { line: 31, cols: \"35-36\" }, DebugSymRef { line: 31, cols: \"16-18\" }], category: Signal, ty: None },\"initial\": SymTableEntry { id: \"initial\", definition_ref: DebugSymRef { start: \"10:13\", end: \"18:14\" }, usages: [], category: State, ty: None },\"middle\": SymTableEntry { id: \"middle\", definition_ref: DebugSymRef { start: \"20:13\", end: \"34:14\" }, usages: [DebugSymRef { line: 15, cols: \"17-23\" }, DebugSymRef { line: 30, cols: \"18-24\" }], category: State, ty: None },\"n\": SymTableEntry { id: \"n\", definition_ref: DebugSymRef { line: 2, cols: \"22-30\" }, usages: [DebugSymRef { line: 16, cols: \"36-37\" }, DebugSymRef { line: 16, cols: \"23-25\" }, DebugSymRef { line: 25, cols: \"26-27\" }, DebugSymRef { line: 27, cols: \"41-42\" }, DebugSymRef { line: 27, cols: \"24-26\" }, DebugSymRef { line: 31, cols: \"48-49\" }, DebugSymRef { line: 31, cols: \"28-30\" }], category: InputSignal, ty: None }", scope: Machine },"//fibo/final": ScopeTable { symbols: "", scope: State },"//fibo/initial": ScopeTable { symbols: "\"c\": SymTableEntry { id: \"c\", definition_ref: DebugSymRef { line: 11, cols: \"14-23\" }, usages: [DebugSymRef { line: 13, cols: \"23-24\" }, DebugSymRef { line: 16, cols: \"33-34\" }], category: Signal, ty: None }", scope: State },"//fibo/middle": ScopeTable { symbols: "\"c\": SymTableEntry { id: \"c\", definition_ref: DebugSymRef { line: 21, cols: \"14-23\" }, usages: [DebugSymRef { line: 23, cols: \"14-15\" }, DebugSymRef { line: 27, cols: \"38-39\" }, DebugSymRef { line: 31, cols: \"45-46\" }], category: Signal, ty: None }", scope: State }, messages: [] }"# ) } } diff --git a/src/compiler/semantic/mod.rs b/src/compiler/semantic/mod.rs index 49ff2b37..63d95851 100644 --- a/src/compiler/semantic/mod.rs +++ b/src/compiler/semantic/mod.rs @@ -1,4 +1,5 @@ use std::{ + cmp::min, collections::HashMap, fmt::{Debug, Display}, }; @@ -48,13 +49,30 @@ pub enum ScopeCategory { /// Information about a symbol #[derive(Clone, Debug)] pub struct SymTableEntry { + pub id: String, pub definition_ref: DebugSymRef, + pub usages: Vec, pub category: SymbolCategory, /// Type pub ty: Option, } impl SymTableEntry { + pub fn new( + id: String, + definition_ref: DebugSymRef, + category: SymbolCategory, + ty: Option, + ) -> Self { + SymTableEntry { + id, + definition_ref, + usages: Vec::new(), + category, + ty, + } + } + pub fn is_scoped(&self) -> bool { matches!( self.category, @@ -78,6 +96,33 @@ impl SymTableEntry { None => "field", } } + + /// Checks if there is a usage of this entry at the given `offset` in the file `filename`. + /// Returns its proximity score if found, otherwise `None`. + fn check_usage_at(&self, filename: &String, offset: usize) -> Option { + for usage in &self.usages { + if let Some(usage_proximity) = usage.proximity_score(filename, offset) { + return Some(usage_proximity); + } + } + None + } + + /// Returns the proximity score of the closest usage or definition + /// to the given `offset` in the file `filename`. + fn proximity_score(&self, filename: &String, offset: usize) -> Option { + let result = min( + self.definition_ref + .proximity_score(filename, offset) + .unwrap_or(usize::MAX), + self.check_usage_at(filename, offset).unwrap_or(usize::MAX), + ); + if result == usize::MAX { + None + } else { + Some(result) + } + } } #[derive(Debug)] @@ -151,6 +196,15 @@ impl ScopeTable { fn add_symbol(&mut self, id: String, entry: SymTableEntry) { self.symbols.insert(id, entry); } + + /// Add a `usage` to the symbol `id`, if symbol exists in scope. + fn add_symbol_usage(&mut self, id: String, usage: DebugSymRef) { + if let Some(symbol) = self.symbols.get(&id) { + let mut updated_symbol = symbol.clone(); + updated_symbol.usages.push(usage); + self.symbols.insert(id, updated_symbol); + } + } } /// Symbol table for a chiquito program @@ -244,6 +298,17 @@ impl SymTable { } } + /// Add a usage of a symbol. + /// The function looks up the parent scopes if symbol is not found in the current scope. + pub fn add_symbol_usage(&mut self, scope: &[String], id: String, usage: DebugSymRef) { + if let Some(found_symbol) = self.find_symbol(scope, id) { + self.scopes + .get_mut(&found_symbol.scope_id) + .unwrap() + .add_symbol_usage(found_symbol.symbol.id, usage); + } + } + /// Add an output variable symbol. /// This is special because if there is an input variable symbol with the same identifier, it /// should create a Input/Output symbol. @@ -289,6 +354,32 @@ impl SymTable { .join("/") } } + + /// Find a `SymTableEntry` by its byte offset in a file. + /// The function can be called externally (e.g., from the language server) + /// + /// ### Parameters + /// - `filename`: The name of the file where the symbol is searched. + /// - `offset`: The byte offset in the file where the symbol is searched. + /// ### Returns + /// The `SymTableEntry` that is closest to the offset. + pub fn find_symbol_by_offset(&self, filename: String, offset: usize) -> Option { + let mut closest_symbol: Option = None; + let mut best_proximity = usize::MAX; + + for scope in self.scopes.values() { + for entry in scope.symbols.values() { + if let Some(proximity) = entry.proximity_score(&filename, offset) + && (proximity < best_proximity) + { + closest_symbol = Some(entry.clone()); + best_proximity = proximity; + } + } + } + + closest_symbol + } } /// Result from running the semantic analyser. @@ -378,3 +469,108 @@ impl RuleSet { .for_each(|rule| rule(analyser, stmt, id, symbol)); } } + +#[cfg(test)] +mod test { + use crate::{ + compiler::semantic::SymTableEntry, + parser::{ast::debug_sym_factory::DebugSymRefFactory, lang}, + }; + + #[test] + fn test_find_usages() { + let circuit = " + machine fibo(signal n) (signal b: field) { + // n and be are created automatically as shared + // signals + signal a: field, i; + + // there is always a state called initial + // input signals get bound to the signal + // in the initial state (first instance) + state initial { + signal c; + + i, a, b, c <== 1, 1, 1, 2; + + -> middle { + a', b', n' <== b, c, n; + } + } + + state middle { + signal c; + + c <== a + b; + + if i + 1 == n { + -> final { + i', b', n' <== i + 1, c, n; + } + } else { + -> middle { + i', a', b', n' <== i + 1, b, c, n; + } + } + } + + // There is always a state called final. + // Output signals get automatically bound to the signals + // with the same name in the final step (last instance). + // This state can be implicit if there are no constraints in it. + } + "; + + let debug_sym_factory = DebugSymRefFactory::new("some", circuit); + let decls = lang::TLDeclsParser::new() + .parse(&debug_sym_factory, circuit) + .unwrap(); + + let result = crate::compiler::semantic::analyser::analyse(&decls); + + let test_cases = [ + (396, "a"), + (397, "a"), + (395, "initial"), + (398, "initial"), + (460, "a"), + (584, "a"), + (772, "a"), + (402, "c"), + (478, "c"), + (578, "c"), + (683, "c"), + (797, "c"), + (468, "n"), + (481, "n"), + (617, "n"), + (669, "n"), + (686, "n"), + (780, "n"), + (800, "n"), + (399, "b"), + (464, "b"), + (475, "b"), + (588, "b"), + (665, "b"), + (776, "b"), + (794, "b"), + (393, "i"), + (608, "i"), + (661, "i"), + (676, "i"), + (768, "i"), + (787, "i"), + (437, "middle"), + (443, "middle"), + ]; + + for (offset, expected_id) in test_cases { + let SymTableEntry { id, .. } = result + .symbols + .find_symbol_by_offset("some".to_string(), offset) + .unwrap(); + assert_eq!(id, expected_id); + } + } +} diff --git a/src/compiler/setup_inter.rs b/src/compiler/setup_inter.rs index ddaf0267..4c3120ff 100644 --- a/src/compiler/setup_inter.rs +++ b/src/compiler/setup_inter.rs @@ -66,7 +66,7 @@ impl SetupInterpreter { if !self.setup.get(&id.name()).unwrap().contains_key("final") { self.interpret_state_decl( dsym, - &Identifier::from("final"), + &Identifier::new("final", dsym.clone()), &Statement::Block(dsym.clone(), vec![]), ) } diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index f8766fc7..225ce1ab 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -82,7 +82,10 @@ impl<'a, F: Field + Hash> Interpreter<'a, F> { self.cur_frame.enter_machine(id.name()); for (id, input_value) in input.iter() { - self.cur_frame.set_value(id, &Value::Field(*input_value)); + self.cur_frame.set_value( + &Identifier::new(id, dsym.clone()), + &Value::Field(*input_value), + ); } while next_state.is_some() { @@ -232,7 +235,7 @@ impl<'a, F: Field + Hash> Interpreter<'a, F> { if self.cur_frame.get_state() == "final" { Some(Statement::StateDecl( machine_dsym.clone(), - self.cur_frame.get_state().into(), + Identifier::new(self.cur_frame.get_state(), machine_dsym.clone()), Box::new(Statement::Block(machine_dsym.clone(), vec![])), )) } else { diff --git a/src/parser/ast/debug_sym_factory.rs b/src/parser/ast/debug_sym_factory.rs index 8c4f6115..cdaad8ea 100644 --- a/src/parser/ast/debug_sym_factory.rs +++ b/src/parser/ast/debug_sym_factory.rs @@ -1,13 +1,13 @@ -use std::rc::Rc; +use std::sync::Arc; -use codespan_reporting::files::{Files, SimpleFile}; +use codespan_reporting::files::SimpleFile; use super::DebugSymRef; /// Factory for creating debug symbol references. pub struct DebugSymRefFactory { /// Source file reference. - file: Rc>, + file: Arc>, } impl DebugSymRefFactory { @@ -22,7 +22,7 @@ impl DebugSymRefFactory { /// /// A new debug symbol reference factory. pub fn new(file_path: &str, contents: &str) -> DebugSymRefFactory { - let file = Rc::new(SimpleFile::new(file_path.to_string(), contents.to_string())); + let file = Arc::new(SimpleFile::new(file_path.to_string(), contents.to_string())); DebugSymRefFactory { file } } @@ -34,47 +34,6 @@ impl DebugSymRefFactory { /// * `start` - Start position of the debug symbol byte reference in the source string. /// * `end` - End position of the debug symbol byte reference in the source string. pub fn create(&self, start: usize, end: usize) -> DebugSymRef { - let start_line_index = self.get_line_index(start); - let start_line_number = self.get_line_number(start_line_index); - let start_col_number = self.get_column_number(start_line_index, start); - - let end_line_index = self.get_line_index(end); - let end_line_number = self.get_line_number(end_line_index); - let end_col_number = self.get_column_number(end_line_index, end); - - DebugSymRef::new( - start_line_number, - start_col_number, - end_line_number, - end_col_number, - Rc::clone(&self.file), - ) - } - - fn get_column_number(&self, line_index: usize, start: usize) -> usize { - match self.file.column_number((), line_index, start) { - Ok(number) => number, - Err(err) => { - panic!("Column number at {} not found: {}", line_index, err); - } - } - } - - fn get_line_index(&self, start: usize) -> usize { - match self.file.line_index((), start) { - Ok(index) => index, - Err(err) => { - panic!("Line index at {} not found: {}", start, err); - } - } - } - - fn get_line_number(&self, line_index: usize) -> usize { - match self.file.line_number((), line_index) { - Ok(number) => number, - Err(err) => { - panic!("Line number at {} not found: {}", line_index, err); - } - } + DebugSymRef::new(start, end, Arc::clone(&self.file)) } } diff --git a/src/parser/ast/mod.rs b/src/parser/ast/mod.rs index fef4ec61..1656e2de 100644 --- a/src/parser/ast/mod.rs +++ b/src/parser/ast/mod.rs @@ -1,7 +1,7 @@ use core::fmt::Debug; -use std::rc::Rc; +use std::sync::Arc; -use codespan_reporting::files::SimpleFile; +use codespan_reporting::files::{Files, SimpleFile}; pub mod debug_sym_factory; pub mod expression; @@ -11,32 +11,78 @@ pub mod tl; /// Debug symbol reference, points to the source file, where a AST node comes from. #[derive(Clone)] pub struct DebugSymRef { - /// Starting line number in the file - pub line_start: usize, - /// Starting column number in the file - pub col_start: usize, - /// Ending line number in the file - pub line_end: usize, - /// Ending column number in the file - pub col_end: usize, + /// Starting byte number in the file + pub start: usize, + /// Ending byte number in the file + pub end: usize, /// Source file reference - file: Rc>, + file: Arc>, } impl DebugSymRef { - pub fn new( - line_start: usize, - col_start: usize, - line_end: usize, - col_end: usize, - file: Rc>, - ) -> DebugSymRef { - DebugSymRef { - line_start, - col_start, - line_end, - col_end, - file, + pub fn new(start: usize, end: usize, file: Arc>) -> DebugSymRef { + DebugSymRef { start, end, file } + } + + fn get_column_number(&self, line_index: usize, start: usize) -> usize { + match self.file.column_number((), line_index, start) { + Ok(number) => number, + Err(err) => { + panic!("Column number at {} not found: {}", line_index, err); + } + } + } + + fn get_line_index(&self, start: usize) -> usize { + match self.file.line_index((), start) { + Ok(index) => index, + Err(err) => { + panic!("Line index at {} not found: {}", start, err); + } + } + } + + fn get_line_number(&self, line_index: usize) -> usize { + match self.file.line_number((), line_index) { + Ok(number) => number, + Err(err) => { + panic!("Line number at {} not found: {}", line_index, err); + } + } + } + + fn get_line_start(&self) -> usize { + let line_idx = self.get_line_index(self.start); + self.get_line_number(line_idx) + } + + fn get_col_start(&self) -> usize { + let line_idx = self.get_line_index(self.start); + self.get_column_number(line_idx, self.start) + } + + fn get_line_end(&self) -> usize { + let line_idx = self.get_line_index(self.end); + self.get_line_number(line_idx) + } + + fn get_col_end(&self) -> usize { + let line_idx = self.get_line_index(self.end); + self.get_column_number(line_idx, self.end) + } + + pub(crate) fn get_filename(&self) -> String { + self.file.name().to_string() + } + + /// Returns the proximity score of the given `offset` to the debug symbol in the file + /// `filename`. The proximity score is calculated as the size of the symbol. + /// If the offset is not within the symbol, returns `None`. + pub fn proximity_score(&self, filename: &String, offset: usize) -> Option { + if self.get_filename() == *filename && self.start <= offset && offset <= self.end { + Some(self.end - self.start) + } else { + None } } } @@ -49,29 +95,54 @@ impl Debug for DebugSymRef { f, "{}:{}:{}", self.file.name(), - self.line_start, - self.col_start + self.get_line_start(), + self.get_col_start() ); } let mut debug_print = f.debug_struct("DebugSymRef"); - if self.line_start == self.line_end { - debug_print - .field("line", &self.line_start) - .field("cols", &format!("{}-{}", self.col_start, self.col_end)); + if self.get_line_start() == self.get_line_end() { + debug_print.field("line", &self.get_line_start()).field( + "cols", + &format!("{}-{}", self.get_col_start(), self.get_col_end()), + ); } else { debug_print - .field("start", &format!("{}:{}", self.line_start, self.col_start)) - .field("end", &format!("{}:{}", self.line_end, self.col_end)); + .field( + "start", + &format!("{}:{}", self.get_line_start(), self.get_col_start()), + ) + .field( + "end", + &format!("{}:{}", self.get_line_end(), self.get_col_end()), + ); } debug_print.finish() } } +impl Eq for DebugSymRef {} + +impl PartialEq for DebugSymRef { + fn eq(&self, other: &Self) -> bool { + self.start == other.start && self.end == other.end && self.file.name() == other.file.name() + } +} + #[derive(Clone, PartialEq, Eq)] -pub struct Identifier(pub String, pub i32); +pub struct Identifier(pub String, pub i32, pub DebugSymRef); +impl Identifier { + pub(crate) fn new>(value: S, dsym: DebugSymRef) -> Self { + let value_str = value.as_ref(); + Identifier(value_str.name(), value_str.rotation(), dsym) + } + + pub(crate) fn debug_sym_ref(&self) -> DebugSymRef { + self.2.clone() + } +} impl Debug for Identifier { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -85,24 +156,12 @@ impl Debug for Identifier { } } -impl From for Identifier { - fn from(value: String) -> Self { - Identifier(value.name(), value.rotation()) - } -} - -impl From<&str> for Identifier { - fn from(value: &str) -> Self { - Identifier::from(value.to_string()) - } -} - pub trait Identifiable { fn rotation(&self) -> i32; fn name(&self) -> String; } -impl Identifiable for String { +impl Identifiable for &str { fn rotation(&self) -> i32 { assert!(!self.is_empty()); let last = self.chars().last().unwrap(); @@ -118,7 +177,7 @@ impl Identifiable for String { let rot = self.rotation(); match rot { - 0 => self.clone(), + 0 => self.to_string(), 1 => { let mut chars = self.chars(); chars.next_back(); @@ -142,18 +201,53 @@ impl Identifiable for Identifier { #[cfg(test)] mod test { - use crate::parser::ast::Identifier; + use std::sync::Arc; + + use codespan_reporting::files::SimpleFile; + + use crate::parser::ast::{DebugSymRef, Identifier}; #[test] fn test_from_string() { - let result = Identifier::from("abc"); + let debug_sym_ref = DebugSymRef { + start: 0, + end: 1, + file: Arc::new(SimpleFile::new("file_path".to_string(), "".to_string())), + }; + let result = Identifier::new("abc", debug_sym_ref.clone()); assert_eq!(result.0, "abc"); assert_eq!(result.1, 0); + assert_eq!(result.2.start, debug_sym_ref.start); + assert_eq!(result.2.end, debug_sym_ref.end); + assert_eq!(*result.2.file.name(), *debug_sym_ref.file.name()); - let result = Identifier::from("abc'"); + let result = Identifier::new("abc'", debug_sym_ref.clone()); assert_eq!(result.0, "abc"); assert_eq!(result.1, 1); + assert_eq!(result.2.start, debug_sym_ref.start); + assert_eq!(result.2.end, debug_sym_ref.end); + assert_eq!(*result.2.file.name(), *debug_sym_ref.file.name()); + } + + #[test] + fn test_proximity_score() { + let file_path = "file_path".to_string(); + let debug_sym_ref = DebugSymRef { + start: 10, + end: 12, + file: Arc::new(SimpleFile::new(file_path.clone(), "".to_string())), + }; + + assert_eq!(debug_sym_ref.proximity_score(&file_path, 9), None); + assert_eq!(debug_sym_ref.proximity_score(&file_path, 10), Some(2)); + assert_eq!( + debug_sym_ref.proximity_score(&"different_file_path".to_string(), 10), + None + ); + assert_eq!(debug_sym_ref.proximity_score(&file_path, 11), Some(2)); + assert_eq!(debug_sym_ref.proximity_score(&file_path, 12), Some(2)); + assert_eq!(debug_sym_ref.proximity_score(&file_path, 13), None); } } diff --git a/src/parser/build.rs b/src/parser/build.rs index 6d3b024d..d832991d 100644 --- a/src/parser/build.rs +++ b/src/parser/build.rs @@ -28,12 +28,12 @@ pub fn build_unary_op, F, V>( } } -pub fn build_query(dsym: DebugSymRef, id: Identifier) -> Expression { +pub fn build_query(id: Identifier, dsym: DebugSymRef) -> Expression { Expression::Query(dsym, id) } -pub fn build_identifier>(id: S) -> Identifier { - Identifier::from(id.into()) +pub fn build_identifier>(id: S, dsym: DebugSymRef) -> Identifier { + Identifier::new(id.into(), dsym) } pub fn build_assert_eq( diff --git a/src/parser/chiquito.lalrpop b/src/parser/chiquito.lalrpop index 5146c368..b8fb582c 100644 --- a/src/parser/chiquito.lalrpop +++ b/src/parser/chiquito.lalrpop @@ -197,7 +197,7 @@ ParsePrefix: Expr = { } ExpressionTerm: Expr = { - => build_query(dsym_factory.create(l,r), id), + => build_query(id, dsym_factory.create(l,r)), => lit, "true" => Expression::True(dsym_factory.create(l,r)), "false" => Expression::False(dsym_factory.create(l,r)), @@ -211,7 +211,7 @@ ParseBinOp: Expr = { } ParseVar: Expr = { - => build_query(dsym_factory.create(l,r), id), + => build_query(id, dsym_factory.create(l,r)), } // Lists @@ -246,7 +246,7 @@ ParseTypedId: TypedIdDecl = { // Terminals Identifier: Identifier = { - r"[a-zA-Z_][a-zA-Z$_0-9@]*[\']?" => build_identifier(<>), + => build_identifier(id, dsym_factory.create(l,r)), } FieldElement: Expr = { diff --git a/test/circuit.chiquito b/test/circuit.chiquito index d7c7dd0c..25a90df9 100644 --- a/test/circuit.chiquito +++ b/test/circuit.chiquito @@ -23,7 +23,7 @@ machine fibo(signal n) (signal b: field) { if i + 1 == n { -> final { - i', b', n' <== i + 1, c, n; + i', b', n' <== i + 1, c, n; } } else { -> middle { diff --git a/test/circuit_error.chiquito b/test/circuit_error.chiquito index 18c8e650..20bb1f3c 100644 --- a/test/circuit_error.chiquito +++ b/test/circuit_error.chiquito @@ -18,7 +18,7 @@ machine fibo(signal n) (signal b: field) { state middle { c <== a + b; - + if i + 1 == n { -> final { i', b', n' <== i + 1, c, n;