nac3ast: add file path to ast location

This commit is contained in:
ychenfo 2021-12-27 02:44:31 +08:00
parent 0ff995722c
commit e32837191a
6 changed files with 49 additions and 32 deletions

View File

@ -97,10 +97,10 @@ impl Nac3 {
)) ))
})?; })?;
let source = fs::read_to_string(source_file).map_err(|e| { let source = fs::read_to_string(&source_file).map_err(|e| {
exceptions::PyIOError::new_err(format!("failed to read input file: {}", e)) exceptions::PyIOError::new_err(format!("failed to read input file: {}", e))
})?; })?;
let parser_result = parser::parse_program(&source) let parser_result = parser::parse_program(&source, source_file.into())
.map_err(|e| exceptions::PySyntaxError::new_err(format!("parse error: {}", e)))?; .map_err(|e| exceptions::PySyntaxError::new_err(format!("parse error: {}", e)))?;
for mut stmt in parser_result.into_iter() { for mut stmt in parser_result.into_iter() {
@ -476,7 +476,7 @@ impl Nac3 {
arg_names.join(", ") arg_names.join(", ")
) )
}; };
let mut synthesized = parse_program(&synthesized).unwrap(); let mut synthesized = parse_program(&synthesized, Default::default()).unwrap();
let resolver = Arc::new(Resolver(Arc::new(InnerResolver { let resolver = Arc::new(Resolver(Arc::new(InnerResolver {
id_to_type: self.builtins_ty.clone().into(), id_to_type: self.builtins_ty.clone().into(),
id_to_def: self.builtins_def.clone().into(), id_to_def: self.builtins_def.clone().into(),

View File

@ -9,6 +9,6 @@ mod impls;
mod location; mod location;
pub use ast_gen::*; pub use ast_gen::*;
pub use location::Location; pub use location::{Location, FileName};
pub type Suite<U = ()> = Vec<Stmt<U>>; pub type Suite<U = ()> = Vec<Stmt<U>>;

View File

@ -1,17 +1,32 @@
//! Datatypes to support source location information. //! Datatypes to support source location information.
use crate::ast_gen::StrRef;
use std::fmt; use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FileName(StrRef);
impl Default for FileName {
fn default() -> Self {
FileName("unknown file".into())
}
}
impl From<String> for FileName {
fn from(s: String) -> Self {
FileName(s.into())
}
}
/// A location somewhere in the sourcecode. /// A location somewhere in the sourcecode.
#[derive(Clone, Copy, Debug, Default, PartialEq)] #[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Location { pub struct Location {
row: usize, row: usize,
column: usize, column: usize,
file: FileName
} }
impl fmt::Display for Location { impl fmt::Display for Location {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "line {} column {}", self.row, self.column) write!(f, "{}:{}:{}", self.file.0, self.row, self.column)
} }
} }
@ -47,8 +62,8 @@ impl Location {
} }
impl Location { impl Location {
pub fn new(row: usize, column: usize) -> Self { pub fn new(row: usize, column: usize, file: FileName) -> Self {
Location { row, column } Location { row, column, file }
} }
pub fn row(&self) -> usize { pub fn row(&self) -> usize {

View File

@ -3,7 +3,7 @@
//! This means source code is translated into separate tokens. //! This means source code is translated into separate tokens.
pub use super::token::Tok; pub use super::token::Tok;
use crate::ast::Location; use crate::ast::{Location, FileName};
use crate::error::{LexicalError, LexicalErrorType}; use crate::error::{LexicalError, LexicalErrorType};
use num_bigint::BigInt; use num_bigint::BigInt;
use num_traits::identities::Zero; use num_traits::identities::Zero;
@ -113,8 +113,8 @@ pub type Spanned = (Location, Tok, Location);
pub type LexResult = Result<Spanned, LexicalError>; pub type LexResult = Result<Spanned, LexicalError>;
#[inline] #[inline]
pub fn make_tokenizer(source: &str) -> impl Iterator<Item = LexResult> + '_ { pub fn make_tokenizer(source: &str, file: FileName) -> impl Iterator<Item = LexResult> + '_ {
make_tokenizer_located(source, Location::new(0, 0)) make_tokenizer_located(source, Location::new(0, 0, file))
} }
pub fn make_tokenizer_located( pub fn make_tokenizer_located(
@ -1320,6 +1320,8 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::default;
use super::{make_tokenizer, NewlineHandler, Tok}; use super::{make_tokenizer, NewlineHandler, Tok};
use num_bigint::BigInt; use num_bigint::BigInt;
@ -1328,7 +1330,7 @@ mod tests {
const UNIX_EOL: &str = "\n"; const UNIX_EOL: &str = "\n";
pub fn lex_source(source: &str) -> Vec<Tok> { pub fn lex_source(source: &str) -> Vec<Tok> {
let lexer = make_tokenizer(source); let lexer = make_tokenizer(source, Default::default());
lexer.map(|x| x.unwrap().1).collect() lexer.map(|x| x.unwrap().1).collect()
} }

View File

@ -7,7 +7,7 @@
use std::iter; use std::iter;
use crate::ast; use crate::ast::{self, FileName};
use crate::error::ParseError; use crate::error::ParseError;
use crate::lexer; use crate::lexer;
pub use crate::mode::Mode; pub use crate::mode::Mode;
@ -20,8 +20,8 @@ use crate::python;
*/ */
/// Parse a full python program, containing usually multiple lines. /// Parse a full python program, containing usually multiple lines.
pub fn parse_program(source: &str) -> Result<ast::Suite, ParseError> { pub fn parse_program(source: &str, file: FileName) -> Result<ast::Suite, ParseError> {
parse(source, Mode::Module).map(|top| match top { parse(source, Mode::Module, file).map(|top| match top {
ast::Mod::Module { body, .. } => body, ast::Mod::Module { body, .. } => body,
_ => unreachable!(), _ => unreachable!(),
}) })
@ -64,15 +64,15 @@ pub fn parse_program(source: &str) -> Result<ast::Suite, ParseError> {
/// ///
/// ``` /// ```
pub fn parse_expression(source: &str) -> Result<ast::Expr, ParseError> { pub fn parse_expression(source: &str) -> Result<ast::Expr, ParseError> {
parse(source, Mode::Expression).map(|top| match top { parse(source, Mode::Expression, Default::default()).map(|top| match top {
ast::Mod::Expression { body } => *body, ast::Mod::Expression { body } => *body,
_ => unreachable!(), _ => unreachable!(),
}) })
} }
// Parse a given source code // Parse a given source code
pub fn parse(source: &str, mode: Mode) -> Result<ast::Mod, ParseError> { pub fn parse(source: &str, mode: Mode, file: FileName) -> Result<ast::Mod, ParseError> {
let lxr = lexer::make_tokenizer(source); let lxr = lexer::make_tokenizer(source, file);
let marker_token = (Default::default(), mode.to_marker(), Default::default()); let marker_token = (Default::default(), mode.to_marker(), Default::default());
let tokenizer = iter::once(Ok(marker_token)).chain(lxr); let tokenizer = iter::once(Ok(marker_token)).chain(lxr);
@ -87,42 +87,42 @@ mod tests {
#[test] #[test]
fn test_parse_empty() { fn test_parse_empty() {
let parse_ast = parse_program("").unwrap(); let parse_ast = parse_program("", Default::default()).unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_print_hello() { fn test_parse_print_hello() {
let source = String::from("print('Hello world')"); let source = String::from("print('Hello world')");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, Default::default()).unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_print_2() { fn test_parse_print_2() {
let source = String::from("print('Hello world', 2)"); let source = String::from("print('Hello world', 2)");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, Default::default()).unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_kwargs() { fn test_parse_kwargs() {
let source = String::from("my_func('positional', keyword=2)"); let source = String::from("my_func('positional', keyword=2)");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, Default::default()).unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_if_elif_else() { fn test_parse_if_elif_else() {
let source = String::from("if 1: 10\nelif 2: 20\nelse: 30"); let source = String::from("if 1: 10\nelif 2: 20\nelse: 30");
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, Default::default()).unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
#[test] #[test]
fn test_parse_lambda() { fn test_parse_lambda() {
let source = "lambda x, y: x * y"; // lambda(x, y): x * y"; let source = "lambda x, y: x * y"; // lambda(x, y): x * y";
let parse_ast = parse_program(&source).unwrap(); let parse_ast = parse_program(&source, Default::default()).unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
@ -130,7 +130,7 @@ mod tests {
fn test_parse_tuples() { fn test_parse_tuples() {
let source = "a, b = 4, 5"; let source = "a, b = 4, 5";
insta::assert_debug_snapshot!(parse_program(&source).unwrap()); insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
} }
#[test] #[test]
@ -141,7 +141,7 @@ class Foo(A, B):
pass pass
def method_with_default(self, arg='default'): def method_with_default(self, arg='default'):
pass"; pass";
insta::assert_debug_snapshot!(parse_program(&source).unwrap()); insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
} }
#[test] #[test]
@ -184,7 +184,7 @@ while i < 2: # nac3: 4
# nac3: if1 # nac3: if1
if 1: # nac3: if2 if 1: # nac3: if2
3"; 3";
insta::assert_debug_snapshot!(parse_program(&source).unwrap()); insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
} }
#[test] #[test]
@ -197,7 +197,7 @@ while test: # nac3: while3
# nac3: simple assign0 # nac3: simple assign0
a = 3 # nac3: simple assign1 a = 3 # nac3: simple assign1
"; ";
insta::assert_debug_snapshot!(parse_program(&source).unwrap()); insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
} }
#[test] #[test]
@ -216,7 +216,7 @@ if a: # nac3: small2
for i in a: # nac3: for1 for i in a: # nac3: for1
pass pass
"; ";
insta::assert_debug_snapshot!(parse_program(&source).unwrap()); insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
} }
#[test] #[test]
@ -225,6 +225,6 @@ for i in a: # nac3: for1
if a: # nac3: something if a: # nac3: something
a = 3 a = 3
"; ";
assert!(parse_program(&source).is_err()); assert!(parse_program(&source, Default::default()).is_err());
} }
} }

View File

@ -35,7 +35,7 @@ fn main() {
Target::initialize_all(&InitializationConfig::default()); Target::initialize_all(&InitializationConfig::default());
let program = match fs::read_to_string(demo_name + ".py") { let program = match fs::read_to_string(demo_name.clone() + ".py") {
Ok(program) => program, Ok(program) => program,
Err(err) => { Err(err) => {
println!("Cannot open input file: {}", err); println!("Cannot open input file: {}", err);
@ -64,7 +64,7 @@ fn main() {
setup_time.duration_since(start).unwrap().as_millis() setup_time.duration_since(start).unwrap().as_millis()
); );
let parser_result = parser::parse_program(&program).unwrap(); let parser_result = parser::parse_program(&program, format!("{}.py", demo_name).into()).unwrap();
let parse_time = SystemTime::now(); let parse_time = SystemTime::now();
println!( println!(
"parse time: {}ms", "parse time: {}ms",