Add Positional argument error to LexicalError

Fixes #1046
This commit is contained in:
HyeonGyu Lee (Vazrupe)
2019-09-12 16:26:24 +09:00
parent 86103bfd01
commit 0ab46fd2bd
7 changed files with 76 additions and 36 deletions

8
Cargo.lock generated
View File

@@ -220,9 +220,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "chrono"
version = "0.4.6"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1159,7 +1160,7 @@ dependencies = [
"blake2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"caseless 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
"crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1209,6 +1210,7 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode_names2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
"wtf8 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -2033,7 +2035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum caseless 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "808dab3318747be122cb31d36de18d4d1c81277a76f8332a02b81a3d73463d7f"
"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
"checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120"

View File

@@ -321,6 +321,12 @@ pub struct Comprehension {
pub is_async: bool,
}
#[derive(Debug, PartialEq)]
pub struct ArgumentList {
pub args: Vec<Expression>,
pub keywords: Vec<Keyword>,
}
#[derive(Debug, PartialEq)]
pub struct Keyword {
pub name: Option<String>,

View File

@@ -20,6 +20,7 @@ pub enum LexicalErrorType {
StringError,
UnicodeError,
NestingError,
PositionalArgumentError,
UnrecognizedToken { tok: char },
FStringError(FStringErrorType),
OtherError(String),
@@ -32,6 +33,9 @@ impl fmt::Display for LexicalErrorType {
LexicalErrorType::FStringError(error) => write!(f, "Got error in f-string: {}", error),
LexicalErrorType::UnicodeError => write!(f, "Got unexpected unicode"),
LexicalErrorType::NestingError => write!(f, "Got unexpected nesting"),
LexicalErrorType::PositionalArgumentError => {
write!(f, "positional argument follows keyword argument")
}
LexicalErrorType::UnrecognizedToken { tok } => {
write!(f, "Got unexpected token {}", tok)
}
@@ -40,6 +44,12 @@ impl fmt::Display for LexicalErrorType {
}
}
impl From<LexicalError> for LalrpopError<Location, Tok, LexicalError> {
fn from(err: LexicalError) -> Self {
lalrpop_util::ParseError::User { error: err }
}
}
// TODO: consolidate these with ParseError
#[derive(Debug, PartialEq)]
pub struct FStringError {

36
parser/src/function.rs Normal file
View File

@@ -0,0 +1,36 @@
use crate::ast;
use crate::error::{LexicalError, LexicalErrorType};
type FunctionArgument = (Option<Option<String>>, ast::Expression);
pub fn parse_args(func_args: Vec<FunctionArgument>) -> Result<ast::ArgumentList, LexicalError> {
let mut args = vec![];
let mut keywords = vec![];
for (name, value) in func_args {
match name {
Some(n) => {
keywords.push(ast::Keyword { name: n, value });
}
None => {
// Allow starred args after keyword arguments.
if !keywords.is_empty() && !is_starred(&value) {
return Err(LexicalError {
error: LexicalErrorType::PositionalArgumentError,
location: value.location.clone(),
});
}
args.push(value);
}
}
}
Ok(ast::ArgumentList { args, keywords })
}
fn is_starred(exp: &ast::Expression) -> bool {
if let ast::ExpressionType::Starred { .. } = exp.node {
true
} else {
false
}
}

View File

@@ -8,6 +8,7 @@ use lalrpop_util::lalrpop_mod;
pub mod ast;
pub mod error;
mod fstring;
mod function;
pub mod lexer;
pub mod location;
pub mod parser;

View File

@@ -7,6 +7,7 @@ use std::iter::FromIterator;
use crate::ast;
use crate::fstring::parse_located_fstring;
use crate::function::parse_args;
use crate::error::LexicalError;
use crate::lexer;
use crate::location;
@@ -580,7 +581,7 @@ KwargParameter<ArgType>: Option<ast::Parameter> = {
ClassDef: ast::Statement = {
<decorator_list:Decorator*> <location:@L> "class" <name:Identifier> <a:("(" ArgumentList ")")?> ":" <body:Suite> => {
let (bases, keywords) = match a {
Some((_, args, _)) => args,
Some((_, arg, _)) => (arg.args, arg.keywords),
None => (vec![], vec![]),
};
ast::Statement {
@@ -616,14 +617,13 @@ Path: ast::Expression = {
Decorator: ast::Expression = {
"@" <p:Path> <a: (@L "(" ArgumentList ")")?> "\n" => {
match a {
Some((location, _, args, _)) => {
let (args, keywords) = args;
Some((location, _, arg, _)) => {
ast::Expression {
location,
node: ast::ExpressionType::Call {
function: Box::new(p),
args,
keywords,
args: arg.args,
keywords: arg.keywords,
}
}
},
@@ -633,7 +633,7 @@ Decorator: ast::Expression = {
};
YieldExpr: ast::Expression = {
<location:@L> "yield" <value:TestList?> => ast::Expression {
<location:@L> "yield" <value:TestList?> => ast::Expression {
location,
node: ast::ExpressionType::Yield { value: value.map(Box::new) }
},
@@ -847,10 +847,9 @@ AtomExpr: ast::Expression = {
AtomExpr2: ast::Expression = {
Atom,
<f:AtomExpr2> <location:@L> "(" <a:ArgumentList> ")" => {
let (args, keywords) = a;
ast::Expression {
location,
node: ast::ExpressionType::Call { function: Box::new(f), args, keywords }
node: ast::ExpressionType::Call { function: Box::new(f), args: a.args, keywords: a.keywords }
}
},
<e:AtomExpr2> <location:@L> "[" <s:SubscriptList> "]" => ast::Expression {
@@ -1060,31 +1059,10 @@ SingleForComprehension: ast::Comprehension = {
ExpressionNoCond: ast::Expression = OrTest;
ComprehensionIf: ast::Expression = "if" <c:ExpressionNoCond> => c;
ArgumentList: (Vec<ast::Expression>, Vec<ast::Keyword>) = {
<e: Comma<FunctionArgument>> => {
let mut args = vec![];
let mut keywords = vec![];
for (name, value) in e {
match name {
Some(n) => {
keywords.push(ast::Keyword { name: n, value: value });
},
None => {
// Allow starred args after keyword arguments.
let is_starred = if let ast::ExpressionType::Starred { .. } = &value.node {
true
} else {
false
};
if keywords.len() > 0 && !is_starred {
panic!("positional argument follows keyword argument {:?}", keywords);
};
args.push(value);
},
}
}
(args, keywords)
ArgumentList: ast::ArgumentList = {
<e: Comma<FunctionArgument>> =>? {
let arg_list = parse_args(e)?;
Ok(arg_list)
}
};

View File

@@ -1,3 +1,6 @@
from testutils import assertRaises
__name__ = "function"
@@ -88,3 +91,7 @@ def f8() -> int:
return 10
assert f8() == 10
with assertRaises(SyntaxError):
exec('print(keyword=10, 20)')