positional only parameter support

This commit is contained in:
Jeong YunWon
2020-03-01 02:22:50 +09:00
parent 0c00afb6f5
commit aa1d20c388
7 changed files with 65 additions and 28 deletions

View File

@@ -38,8 +38,9 @@ pub struct CodeObject {
pub label_map: HashMap<Label, usize>,
pub locations: Vec<Location>,
pub flags: CodeFlags,
pub arg_names: Vec<String>, // Names of positional arguments
pub varargs: Varargs, // *args or *
pub posonlyarg_count: usize, // Number of positional-only arguments
pub arg_names: Vec<String>, // Names of positional arguments
pub varargs: Varargs, // *args or *
pub kwonlyarg_names: Vec<String>,
pub varkeywords: Varargs, // **kwargs or **
pub source_path: String,
@@ -370,6 +371,7 @@ impl CodeObject {
#[allow(clippy::too_many_arguments)]
pub fn new(
flags: CodeFlags,
posonlyarg_count: usize,
arg_names: Vec<String>,
varargs: Varargs,
kwonlyarg_names: Vec<String>,
@@ -383,6 +385,7 @@ impl CodeObject {
label_map: HashMap::new(),
locations: Vec::new(),
flags,
posonlyarg_count,
arg_names,
varargs,
kwonlyarg_names,

View File

@@ -165,6 +165,7 @@ impl<O: OutputStream> Compiler<O> {
let line_number = self.get_source_line_number();
self.push_output(CodeObject::new(
Default::default(),
0,
Vec::new(),
Varargs::None,
Vec::new(),
@@ -695,6 +696,7 @@ impl<O: OutputStream> Compiler<O> {
let line_number = self.get_source_line_number();
self.push_output(CodeObject::new(
flags,
args.posonlyargs_count,
args.args.iter().map(|a| a.arg.clone()).collect(),
compile_varargs(&args.vararg),
args.kwonlyargs.iter().map(|a| a.arg.clone()).collect(),
@@ -976,6 +978,7 @@ impl<O: OutputStream> Compiler<O> {
let line_number = self.get_source_line_number();
self.push_output(CodeObject::new(
Default::default(),
0,
vec![],
Varargs::None,
vec![],
@@ -1933,6 +1936,7 @@ impl<O: OutputStream> Compiler<O> {
// Create magnificent function <listcomp>:
self.push_output(CodeObject::new(
Default::default(),
1,
vec![".0".to_owned()],
Varargs::None,
vec![],

View File

@@ -374,6 +374,7 @@ impl Expression {
/// distinguish between function parameters and actual call arguments.
#[derive(Debug, PartialEq, Default)]
pub struct Parameters {
pub posonlyargs_count: usize,
pub args: Vec<Parameter>,
pub kwonlyargs: Vec<Parameter>,
pub vararg: Varargs, // Optionally we handle optionally named '*args' or '*'

View File

@@ -7,11 +7,13 @@ type ParameterDefs = (Vec<ast::Parameter>, Vec<ast::Expression>);
type ParameterDef = (ast::Parameter, Option<ast::Expression>);
#[allow(clippy::collapsible_if)]
pub fn parse_params(params: Vec<ParameterDef>) -> Result<ParameterDefs, LexicalError> {
pub fn parse_params(
params: (Vec<ParameterDef>, Vec<ParameterDef>),
) -> Result<ParameterDefs, LexicalError> {
let mut names = vec![];
let mut defaults = vec![];
for (name, default) in params {
let mut try_default = |name: &ast::Parameter, default| {
if let Some(default) = default {
defaults.push(default);
} else {
@@ -20,10 +22,20 @@ pub fn parse_params(params: Vec<ParameterDef>) -> Result<ParameterDefs, LexicalE
// have defaults
return Err(LexicalError {
error: LexicalErrorType::DefaultArgumentError,
location: name.location,
location: name.location.clone(),
});
}
}
Ok(())
};
for (name, default) in params.0 {
try_default(&name, default)?;
names.push(name);
}
for (name, default) in params.1 {
try_default(&name, default)?;
names.push(name);
}

View File

@@ -258,6 +258,7 @@ mod tests {
location: ast::Location::new(1, 1),
node: ast::ExpressionType::Lambda {
args: Box::new(ast::Parameters {
posonlyargs_count: 0,
args: vec![
ast::Parameter {
location: ast::Location::new(1, 8),
@@ -335,6 +336,7 @@ mod tests {
is_async: false,
name: String::from("__init__"),
args: Box::new(ast::Parameters {
posonlyargs_count: 0,
args: vec![ast::Parameter {
location: ast::Location::new(2, 15),
arg: String::from("self"),
@@ -360,6 +362,7 @@ mod tests {
is_async: false,
name: String::from("method_with_default"),
args: Box::new(ast::Parameters {
posonlyargs_count: 0,
args: vec![
ast::Parameter {
location: ast::Location::new(4, 26),

View File

@@ -456,22 +456,25 @@ Parameters: ast::Parameters = {
// once for lambda defs.
ParameterList<ArgType>: ast::Parameters = {
<param1:ParameterDefs<ArgType>> <args2:("," ParameterListStarArgs<ArgType>)?> ","? =>? {
let (names, default_elements) = parse_params(param1)?;
let posonlyargs_count = param1.0.len();
let (names, defaults) = parse_params(param1)?;
// Now gather rest of parameters:
let (vararg, kwonlyargs, kw_defaults, kwarg) = args2.map_or((None, vec![], vec![], None), |x| x.1);
Ok(ast::Parameters {
posonlyargs_count,
args: names,
kwonlyargs,
vararg: vararg.into(),
kwarg: kwarg.into(),
defaults: default_elements,
kw_defaults: kw_defaults,
defaults,
kw_defaults,
})
},
<param1:ParameterDefs<ArgType>> <kw:("," KwargParameter<ArgType>)> ","? =>? {
let (names, default_elements) = parse_params(param1)?;
let posonlyargs_count = param1.0.len();
let (names, defaults) = parse_params(param1)?;
// Now gather rest of parameters:
let vararg = None;
@@ -480,27 +483,30 @@ ParameterList<ArgType>: ast::Parameters = {
let kwarg = Some(kw.1);
Ok(ast::Parameters {
posonlyargs_count,
args: names,
kwonlyargs,
vararg: vararg.into(),
kwarg: kwarg.into(),
defaults: default_elements,
kw_defaults: kw_defaults,
defaults,
kw_defaults,
})
},
<params:ParameterListStarArgs<ArgType>> ","? => {
let (vararg, kwonlyargs, kw_defaults, kwarg) = params;
ast::Parameters {
posonlyargs_count: 0,
args: vec![],
kwonlyargs,
vararg: vararg.into(),
kwarg: kwarg.into(),
defaults: vec![],
kw_defaults: kw_defaults,
kw_defaults,
}
},
<kw:KwargParameter<ArgType>> ","? => {
ast::Parameters {
posonlyargs_count: 0,
args: vec![],
kwonlyargs: vec![],
vararg: ast::Varargs::None,
@@ -513,10 +519,13 @@ ParameterList<ArgType>: ast::Parameters = {
// Use inline here to make sure the "," is not creating an ambiguity.
#[inline]
ParameterDefs<ArgType>: Vec<(ast::Parameter, Option<ast::Expression>)> = {
ParameterDefs<ArgType>: (Vec<(ast::Parameter, Option<ast::Expression>)>, Vec<(ast::Parameter, Option<ast::Expression>)>) = {
<args:OneOrMore<ParameterDef<ArgType>>> => {
args
}
(vec![], args)
},
<pos_args:OneOrMore<ParameterDef<ArgType>>> "," "/" <args:("," ParameterDef<ArgType>)*> => {
(pos_args, args.into_iter().map(|e| e.1).collect())
},
};
ParameterDef<ArgType>: (ast::Parameter, Option<ast::Expression>) = {

View File

@@ -7,11 +7,12 @@ use std::ops::Deref;
use super::objtype::PyClassRef;
use crate::bytecode;
use crate::pyobject::{IdProtocol, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
use crate::pyobject::{IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
use crate::vm::VirtualMachine;
pub type PyCodeRef = PyRef<PyCode>;
#[pyclass]
pub struct PyCode {
pub code: bytecode::CodeObject,
}
@@ -41,12 +42,15 @@ impl PyValue for PyCode {
}
}
#[pyimpl]
impl PyCodeRef {
#[pyslot]
#[allow(clippy::new_ret_no_self)]
fn new(_cls: PyClassRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error("Cannot directly create code object".to_owned()))
}
#[pymethod(magic)]
fn repr(self) -> String {
let code = &self.code;
format!(
@@ -58,22 +62,32 @@ impl PyCodeRef {
)
}
#[pyproperty]
fn co_posonlyargcount(self) -> usize {
self.code.posonlyarg_count
}
#[pyproperty]
fn co_argcount(self) -> usize {
self.code.arg_names.len()
}
#[pyproperty]
fn co_filename(self) -> String {
self.code.source_path.clone()
}
#[pyproperty]
fn co_firstlineno(self) -> usize {
self.code.first_line_number
}
#[pyproperty]
fn co_kwonlyargcount(self) -> usize {
self.code.kwonlyarg_names.len()
}
#[pyproperty]
fn co_consts(self, vm: &VirtualMachine) -> PyObjectRef {
let consts = self
.code
@@ -83,26 +97,17 @@ impl PyCodeRef {
vm.ctx.new_tuple(consts)
}
#[pyproperty]
fn co_name(self) -> String {
self.code.obj_name.clone()
}
#[pyproperty]
fn co_flags(self) -> u8 {
self.code.flags.bits()
}
}
pub fn init(ctx: &PyContext) {
extend_class!(ctx, &ctx.types.code_type, {
(slot new) => PyCodeRef::new,
"__repr__" => ctx.new_method(PyCodeRef::repr),
"co_argcount" => ctx.new_readonly_getset("co_argcount", PyCodeRef::co_argcount),
"co_consts" => ctx.new_readonly_getset("co_consts", PyCodeRef::co_consts),
"co_filename" => ctx.new_readonly_getset("co_filename", PyCodeRef::co_filename),
"co_firstlineno" => ctx.new_readonly_getset("co_firstlineno", PyCodeRef::co_firstlineno),
"co_kwonlyargcount" => ctx.new_readonly_getset("co_kwonlyargcount", PyCodeRef::co_kwonlyargcount),
"co_name" => ctx.new_readonly_getset("co_name", PyCodeRef::co_name),
"co_flags" => ctx.new_readonly_getset("co_flags", PyCodeRef::co_flags),
});
PyCodeRef::extend_class(ctx, &ctx.types.code_type);
}