diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 77c31b6e..6f236e77 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -1,6 +1,7 @@ use crate::{ builtins::{self, bool, g1_element, g2_element}, expr::{TypedExpr, UntypedExpr}, + line_numbers::LineNumbers, parser::token::{Base, Token}, tipo::{PatternConstructor, Type, TypeInfo}, }; @@ -42,6 +43,7 @@ pub struct Module { pub docs: Vec, pub type_info: Info, pub definitions: Vec, + pub lines: LineNumbers, pub kind: ModuleKind, } diff --git a/crates/aiken-lang/src/gen_uplc.rs b/crates/aiken-lang/src/gen_uplc.rs index fe32bebc..3624a844 100644 --- a/crates/aiken-lang/src/gen_uplc.rs +++ b/crates/aiken-lang/src/gen_uplc.rs @@ -27,10 +27,11 @@ use crate::{ gen_uplc::builder::{ check_replaceable_opaque_type, convert_opaque_type, erase_opaque_type_operations, find_and_replace_generics, find_list_clause_or_default_first, get_arg_type_name, - get_generic_id_and_type, get_generic_variant_name, get_src_code_by_span, monomorphize, - pattern_has_conditions, wrap_as_multi_validator, wrap_validator_condition, CodeGenFunction, - SpecificClause, + get_generic_id_and_type, get_generic_variant_name, get_line_columns_by_span, + get_src_code_by_span, monomorphize, pattern_has_conditions, wrap_as_multi_validator, + wrap_validator_condition, CodeGenFunction, SpecificClause, }, + line_numbers::LineNumbers, tipo::{ ModuleValueConstructor, PatternConstructor, Type, TypeInfo, ValueConstructor, ValueConstructorVariant, @@ -55,7 +56,7 @@ pub struct CodeGenerator<'a> { functions: IndexMap, data_types: IndexMap, module_types: IndexMap<&'a String, &'a TypeInfo>, - module_src: IndexMap, + module_src: IndexMap, /// immutable option tracing: TraceLevel, /// mutable index maps that are reset @@ -74,7 +75,7 @@ impl<'a> CodeGenerator<'a> { functions: IndexMap, data_types: IndexMap, module_types: IndexMap<&'a String, &'a TypeInfo>, - module_src: IndexMap, + module_src: IndexMap, tracing: TraceLevel, ) -> Self { CodeGenerator { @@ -132,10 +133,10 @@ impl<'a> CodeGenerator<'a> { air_tree_fun = wrap_validator_condition(air_tree_fun, self.tracing); - let src_code = self.module_src.get(module_name).unwrap().clone(); + let (src_code, lines) = self.module_src.get(module_name).unwrap().clone(); let mut validator_args_tree = - self.check_validator_args(&fun.arguments, true, air_tree_fun, &src_code); + self.check_validator_args(&fun.arguments, true, air_tree_fun, &src_code, &lines); validator_args_tree = AirTree::no_op().hoist_over(validator_args_tree); @@ -154,8 +155,13 @@ impl<'a> CodeGenerator<'a> { air_tree_fun_other = wrap_validator_condition(air_tree_fun_other, self.tracing); - let mut validator_args_tree_other = - self.check_validator_args(&other.arguments, true, air_tree_fun_other, &src_code); + let mut validator_args_tree_other = self.check_validator_args( + &other.arguments, + true, + air_tree_fun_other, + &src_code, + &lines, + ); validator_args_tree_other = AirTree::no_op().hoist_over(validator_args_tree_other); @@ -474,8 +480,12 @@ impl<'a> CodeGenerator<'a> { if kind.is_expect() { let msg = match self.tracing { TraceLevel::Silent => unreachable!("excluded from pattern guards"), - // TODO: line number & col - TraceLevel::Compact => format!("{}", location.start), + TraceLevel::Compact => get_line_columns_by_span( + module_name, + location, + &self.module_src, + ) + .to_string(), TraceLevel::Verbose => { get_src_code_by_span(module_name, location, &self.module_src) } @@ -2746,6 +2756,7 @@ impl<'a> CodeGenerator<'a> { has_context: bool, body: AirTree, src_code: &str, + lines: &LineNumbers, ) -> AirTree { let checked_args = arguments .iter() @@ -2764,8 +2775,10 @@ impl<'a> CodeGenerator<'a> { TraceLevel::Compact | TraceLevel::Verbose => { let msg = match self.tracing { TraceLevel::Silent => unreachable!("excluded from pattern guards"), - // TODO: Show line number + column - TraceLevel::Compact => format!("{}", arg_span.start), + TraceLevel::Compact => lines + .line_and_column_number(arg_span.start) + .expect("Out of bounds span") + .to_string(), TraceLevel::Verbose => src_code .get(arg_span.start..arg_span.end) .expect("Out of bounds span") diff --git a/crates/aiken-lang/src/gen_uplc/builder.rs b/crates/aiken-lang/src/gen_uplc/builder.rs index 2970ab32..bd351a40 100644 --- a/crates/aiken-lang/src/gen_uplc/builder.rs +++ b/crates/aiken-lang/src/gen_uplc/builder.rs @@ -20,6 +20,7 @@ use crate::{ }, builtins::{bool, data, function, int, list, string, void}, expr::TypedExpr, + line_numbers::{LineColumn, LineNumbers}, tipo::{PatternConstructor, TypeVar, ValueConstructor, ValueConstructorVariant}, }; @@ -1775,9 +1776,9 @@ pub fn extract_constant(term: &Term) -> Option> { pub fn get_src_code_by_span( module_name: &String, span: &Span, - module_src: &IndexMap, + module_src: &IndexMap, ) -> String { - let src = module_src + let (src, _) = module_src .get(module_name) .unwrap_or_else(|| panic!("Missing module {module_name}")); @@ -1786,6 +1787,20 @@ pub fn get_src_code_by_span( .to_string() } +pub fn get_line_columns_by_span( + module_name: &String, + span: &Span, + module_src: &IndexMap, +) -> LineColumn { + let (_, lines) = module_src + .get(module_name) + .unwrap_or_else(|| panic!("Missing module {module_name}")); + + lines + .line_and_column_number(span.start) + .expect("Out of bounds span") +} + pub fn air_holds_msg(air: &Air) -> bool { match air { Air::AssertConstr { .. } | Air::AssertBool { .. } | Air::FieldsEmpty | Air::ListEmpty => { diff --git a/crates/aiken-lang/src/line_numbers.rs b/crates/aiken-lang/src/line_numbers.rs index 643cd972..cefc509b 100644 --- a/crates/aiken-lang/src/line_numbers.rs +++ b/crates/aiken-lang/src/line_numbers.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display}; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct LineNumbers { line_starts: Vec, length: usize, diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index a3813a82..451683c2 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -15,7 +15,7 @@ pub use definition::parser as definition; pub use expr::parser as expression; pub use pattern::parser as pattern; -use crate::ast; +use crate::{ast, line_numbers::LineNumbers}; use chumsky::prelude::*; use error::ParseError; use extra::ModuleExtra; @@ -30,8 +30,11 @@ pub fn module( let definitions = definition().repeated().then_ignore(end()).parse(stream)?; + let lines = LineNumbers::new(src); + let module = ast::UntypedModule { kind, + lines, definitions, docs: vec![], name: "".to_string(), diff --git a/crates/aiken-lang/src/snapshots/can_handle_comments_at_end_of_file.snap b/crates/aiken-lang/src/snapshots/can_handle_comments_at_end_of_file.snap index c4b7b173..30f7d44c 100644 --- a/crates/aiken-lang/src/snapshots/can_handle_comments_at_end_of_file.snap +++ b/crates/aiken-lang/src/snapshots/can_handle_comments_at_end_of_file.snap @@ -19,5 +19,17 @@ Module { }, ), ], + lines: LineNumbers { + line_starts: [ + 0, + 10, + 11, + 27, + ], + length: 43, + last: Some( + 27, + ), + }, kind: Validator, } diff --git a/crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap b/crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap index 08fec703..85167624 100644 --- a/crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap +++ b/crates/aiken-lang/src/snapshots/function_ambiguous_sequence.snap @@ -197,5 +197,32 @@ Module { }, ), ], + lines: LineNumbers { + line_starts: [ + 0, + 13, + 27, + 34, + 36, + 37, + 50, + 64, + 71, + 73, + 74, + 87, + 104, + 106, + 107, + 120, + 138, + 154, + 156, + ], + length: 156, + last: Some( + 156, + ), + }, kind: Validator, } diff --git a/crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap b/crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap index b7e86215..a0708406 100644 --- a/crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap +++ b/crates/aiken-lang/src/snapshots/parse_unicode_offset_1.snap @@ -48,5 +48,18 @@ Module { }, ), ], + lines: LineNumbers { + line_starts: [ + 0, + 11, + 27, + 31, + 33, + ], + length: 33, + last: Some( + 33, + ), + }, kind: Validator, } diff --git a/crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap b/crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap index b8bb738f..699df711 100644 --- a/crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap +++ b/crates/aiken-lang/src/snapshots/parse_unicode_offset_2.snap @@ -46,5 +46,18 @@ Module { }, ), ], + lines: LineNumbers { + line_starts: [ + 0, + 11, + 25, + 29, + 31, + ], + length: 31, + last: Some( + 31, + ), + }, kind: Validator, } diff --git a/crates/aiken-lang/src/snapshots/windows_newline.snap b/crates/aiken-lang/src/snapshots/windows_newline.snap index 70e7d0e6..a878badf 100644 --- a/crates/aiken-lang/src/snapshots/windows_newline.snap +++ b/crates/aiken-lang/src/snapshots/windows_newline.snap @@ -20,5 +20,15 @@ Module { }, ), ], + lines: LineNumbers { + line_starts: [ + 0, + 16, + ], + length: 16, + last: Some( + 16, + ), + }, kind: Validator, } diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index b3cdc96f..693b8257 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -1,3 +1,4 @@ +use crate::line_numbers::LineNumbers; use std::{cmp::Ordering, collections::HashMap, rc::Rc}; use vec1::Vec1; @@ -26,6 +27,8 @@ use super::{ #[derive(Debug)] pub(crate) struct ExprTyper<'a, 'b> { + pub(crate) lines: &'a LineNumbers, + pub(crate) environment: &'a mut Environment<'b>, // We tweak the tracing behavior during type-check. Traces are either kept or left out of the @@ -435,7 +438,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> { TraceLevel::Compact => Some(TypedExpr::String { location, tipo: string(), - value: format!("{}", location.start), + value: self + .lines + .line_and_column_number(location.start) + .expect("Spans are within bounds.") + .to_string(), }), TraceLevel::Silent => None, }; @@ -1827,7 +1834,6 @@ impl<'a, 'b> ExprTyper<'a, 'b> { match self.tracing.trace_level(false) { TraceLevel::Silent => Ok(then), - // TODO: use line numbers & cols TraceLevel::Compact => Ok(TypedExpr::Trace { location, tipo, @@ -1835,7 +1841,11 @@ impl<'a, 'b> ExprTyper<'a, 'b> { text: Box::new(TypedExpr::String { location, tipo: string(), - value: format!("{}", location.start), + value: self + .lines + .line_and_column_number(location.start) + .expect("Spans are within bounds.") + .to_string(), }), }), TraceLevel::Verbose => Ok(TypedExpr::Trace { @@ -1995,12 +2005,17 @@ impl<'a, 'b> ExprTyper<'a, 'b> { self.environment.instantiate(t, ids, &self.hydrator) } - pub fn new(environment: &'a mut Environment<'b>, tracing: Tracing) -> Self { + pub fn new( + environment: &'a mut Environment<'b>, + lines: &'a LineNumbers, + tracing: Tracing, + ) -> Self { Self { hydrator: Hydrator::new(), environment, tracing, ungeneralised_function_used: false, + lines, } } diff --git a/crates/aiken-lang/src/tipo/infer.rs b/crates/aiken-lang/src/tipo/infer.rs index 782540ce..5e992fa5 100644 --- a/crates/aiken-lang/src/tipo/infer.rs +++ b/crates/aiken-lang/src/tipo/infer.rs @@ -8,6 +8,7 @@ use crate::{ }, builtins, builtins::function, + line_numbers::LineNumbers, IdGenerator, }; @@ -79,8 +80,14 @@ impl UntypedModule { } for def in consts.into_iter().chain(not_consts) { - let definition = - infer_definition(def, &name, &mut hydrators, &mut environment, tracing)?; + let definition = infer_definition( + def, + &name, + &mut hydrators, + &mut environment, + &self.lines, + tracing, + )?; definitions.push(definition); } @@ -127,6 +134,7 @@ impl UntypedModule { name: name.clone(), definitions, kind, + lines: self.lines, type_info: TypeInfo { name, types, @@ -145,6 +153,7 @@ fn infer_definition( module_name: &String, hydrators: &mut HashMap, environment: &mut Environment<'_>, + lines: &LineNumbers, tracing: Tracing, ) -> Result { match def { @@ -181,7 +190,7 @@ fn infer_definition( .map(|(arg_name, tipo)| arg_name.set_type(tipo.clone())) .collect(); - let mut expr_typer = ExprTyper::new(environment, tracing); + let mut expr_typer = ExprTyper::new(environment, lines, tracing); expr_typer.hydrator = hydrators .remove(&name) @@ -293,6 +302,7 @@ fn infer_definition( module_name, hydrators, environment, + lines, tracing, )? else { @@ -343,6 +353,7 @@ fn infer_definition( module_name, hydrators, environment, + lines, tracing, )? else { @@ -404,6 +415,7 @@ fn infer_definition( module_name, hydrators, environment, + lines, tracing, )? { environment.unify(f.return_type.clone(), builtins::bool(), f.location, false)?; @@ -585,7 +597,7 @@ fn infer_definition( .. }) => { let typed_expr = - ExprTyper::new(environment, tracing).infer_const(&annotation, *value)?; + ExprTyper::new(environment, lines, tracing).infer_const(&annotation, *value)?; let tipo = typed_expr.tipo(); diff --git a/crates/aiken-project/src/module.rs b/crates/aiken-project/src/module.rs index 5737be52..f6157d66 100644 --- a/crates/aiken-project/src/module.rs +++ b/crates/aiken-project/src/module.rs @@ -8,6 +8,7 @@ use aiken_lang::{ builder::{DataTypeKey, FunctionAccessKey}, CodeGenerator, }, + line_numbers::LineNumbers, parser::extra::{comments_before, Comment, ModuleExtra}, tipo::TypeInfo, }; @@ -401,7 +402,10 @@ impl CheckedModules { | Definition::Use(_) => {} } } - module_src.insert(module.name.clone(), module.code.clone()); + module_src.insert( + module.name.clone(), + (module.code.clone(), LineNumbers::new(&module.code)), + ); } let mut module_types_index = IndexMap::new();