Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mruby
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
mruby
Commits
ff1ac49a
Unverified
Commit
ff1ac49a
authored
Oct 06, 2019
by
Yukihiro "Matz" Matsumoto
Committed by
GitHub
Oct 06, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #4754 from dearblue/get-keyword-args
Get keyword arguments with `mrb_get_args()`
parents
ffaa2968
5f929d69
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
134 additions
and
1 deletion
+134
-1
include/mruby.h
include/mruby.h
+57
-0
src/class.c
src/class.c
+77
-1
No files found.
include/mruby.h
View file @
ff1ac49a
...
...
@@ -870,11 +870,67 @@ MRB_API struct RClass * mrb_define_module_under(mrb_state *mrb, struct RClass *o
* | `*` | rest arguments | {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array; `*!` avoid copy of the stack. |
* | <code>\|</code> | optional | | After this spec following specs would be optional. |
* | `?` | optional given | {mrb_bool} | `TRUE` if preceding argument is given. Used to check optional argument is given. |
* | `:` | keyword args | {mrb_kwargs} const | Get keyword arguments. @see mrb_kwargs |
*
* @see mrb_get_args
*/
typedef
const
char
*
mrb_args_format
;
/**
* Get keyword arguments by `mrb_get_args()` with `:` specifier.
*
* `mrb_kwargs::num` indicates that the number of keyword values.
*
* `mrb_kwargs::values` is an object array, and the keyword argument corresponding to the string array is assigned.
* Note that `undef` is assigned if there is no keyword argument corresponding to `mrb_kwargs::optional`.
*
* `mrb_kwargs::table` accepts a string array.
*
* `mrb_kwargs::required` indicates that the specified number of keywords starting from the beginning of the string array are required.
*
* `mrb_kwargs::rest` is the remaining keyword argument that can be accepted as `**rest` in Ruby.
* If `NULL` is specified, `ArgumentError` is raised when there is an undefined keyword.
*
* Examples:
*
* // def method(a: 1, b: 2)
*
* uint32_t kw_num = 2;
* const char *kw_names[kw_num] = { "a", "b" };
* uint32_t kw_required = 0;
* mrb_value kw_values[kw_num];
* const mrb_kwargs kwargs = { kw_num, kw_values, kw_names, kw_required, NULL };
*
* mrb_get_args(mrb, ":", &kwargs);
* if (mrb_undef_p(kw_values[0])) { kw_values[0] = mrb_fixnum_value(1); }
* if (mrb_undef_p(kw_values[1])) { kw_values[1] = mrb_fixnum_value(2); }
*
*
* // def method(str, x:, y: 2, z: "default string", **opts)
*
* mrb_value str, kw_rest;
* uint32_t kw_num = 3;
* const char *kw_names[kw_num] = { "x", "y", "z" };
* uint32_t kw_required = 1;
* mrb_value kw_values[kw_num];
* const mrb_kwargs kwargs = { kw_num, kw_values, kw_names, kw_required, &kw_rest };
*
* mrb_get_args(mrb, "S:", &str, &kwargs);
* // or: mrb_get_args(mrb, ":S", &kwargs, &str);
* if (mrb_undef_p(kw_values[1])) { kw_values[1] = mrb_fixnum_value(2); }
* if (mrb_undef_p(kw_values[2])) { kw_values[2] = mrb_str_new_cstr(mrb, "default string"); }
*/
typedef
struct
mrb_kwargs
mrb_kwargs
;
struct
mrb_kwargs
{
uint32_t
num
;
mrb_value
*
values
;
const
char
*
const
*
table
;
uint32_t
required
;
mrb_value
*
rest
;
};
/**
* Retrieve arguments from mrb_state.
*
...
...
@@ -883,6 +939,7 @@ typedef const char *mrb_args_format;
* @param ... The passing variadic arguments must be a pointer of retrieving type.
* @return the number of arguments retrieved.
* @see mrb_args_format
* @see mrb_kwargs
*/
MRB_API
mrb_int
mrb_get_args
(
mrb_state
*
mrb
,
mrb_args_format
format
,
...);
...
...
src/class.c
View file @
ff1ac49a
...
...
@@ -7,6 +7,7 @@
#include <stdarg.h>
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/hash.h>
#include <mruby/class.h>
#include <mruby/numeric.h>
#include <mruby/proc.h>
...
...
@@ -549,6 +550,8 @@ mrb_get_argv(mrb_state *mrb)
return
array_argv
;
}
void
mrb_hash_check_kdict
(
mrb_state
*
mrb
,
mrb_value
self
);
/*
retrieve arguments from mrb_state.
...
...
@@ -578,6 +581,7 @@ mrb_get_argv(mrb_state *mrb)
*: rest argument [mrb_value*,mrb_int] The rest of the arguments as an array; *! avoid copy of the stack
|: optional Following arguments are optional
?: optional given [mrb_bool] true if preceding argument (optional) is given
':': keyword args [mrb_kwargs const] Get keyword arguments
*/
MRB_API
mrb_int
mrb_get_args
(
mrb_state
*
mrb
,
const
char
*
format
,
...)
...
...
@@ -592,6 +596,9 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_bool
opt
=
FALSE
;
mrb_bool
opt_skip
=
TRUE
;
mrb_bool
given
=
TRUE
;
mrb_value
kdict
;
mrb_bool
reqkarg
=
FALSE
;
mrb_int
needargc
=
0
;
va_start
(
ap
,
format
);
...
...
@@ -605,18 +612,31 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
break
;
case
'*'
:
opt_skip
=
FALSE
;
if
(
!
reqkarg
)
reqkarg
=
strchr
(
fmt
,
':'
)
?
TRUE
:
FALSE
;
goto
check_exit
;
case
'!'
:
break
;
case
'&'
:
case
'?'
:
if
(
opt
)
opt_skip
=
FALSE
;
break
;
case
':'
:
reqkarg
=
TRUE
;
break
;
default:
if
(
!
opt
)
needargc
++
;
break
;
}
}
check_exit:
if
(
reqkarg
&&
argc
>
needargc
&&
mrb_hash_p
(
kdict
=
ARGV
[
argc
-
1
]))
{
mrb_hash_check_kdict
(
mrb
,
kdict
);
argc
--
;
}
else
{
kdict
=
mrb_nil_value
();
}
opt
=
FALSE
;
i
=
0
;
while
((
c
=
*
format
++
))
{
...
...
@@ -624,7 +644,7 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
mrb_bool
altmode
;
switch
(
c
)
{
case
'|'
:
case
'*'
:
case
'&'
:
case
'?'
:
case
'|'
:
case
'*'
:
case
'&'
:
case
'?'
:
case
':'
:
break
;
default:
if
(
argc
<=
i
)
{
...
...
@@ -932,6 +952,62 @@ mrb_get_args(mrb_state *mrb, const char *format, ...)
}
}
break
;
case
':'
:
{
mrb_value
ksrc
=
mrb_hash_p
(
kdict
)
?
mrb_hash_dup
(
mrb
,
kdict
)
:
mrb_hash_new
(
mrb
);
const
mrb_kwargs
*
kwargs
=
va_arg
(
ap
,
const
mrb_kwargs
*
);
mrb_value
*
rest
;
if
(
kwargs
==
NULL
)
{
rest
=
NULL
;
}
else
{
uint32_t
kwnum
=
kwargs
->
num
;
uint32_t
required
=
kwargs
->
required
;
const
char
*
const
*
kname
=
kwargs
->
table
;
mrb_value
*
values
=
kwargs
->
values
;
uint32_t
j
;
const
uint32_t
keyword_max
=
40
;
if
(
kwnum
>
keyword_max
||
required
>
kwnum
)
{
mrb_raise
(
mrb
,
E_ARGUMENT_ERROR
,
"keyword number is too large"
);
}
for
(
j
=
required
;
j
>
0
;
j
--
,
kname
++
,
values
++
)
{
mrb_value
k
=
mrb_symbol_value
(
mrb_intern_cstr
(
mrb
,
*
kname
));
if
(
!
mrb_hash_key_p
(
mrb
,
ksrc
,
k
))
{
mrb_raisef
(
mrb
,
E_ARGUMENT_ERROR
,
"missing keyword: %s"
,
*
kname
);
}
*
values
=
mrb_hash_delete_key
(
mrb
,
ksrc
,
k
);
mrb_gc_protect
(
mrb
,
*
values
);
}
for
(
j
=
kwnum
-
required
;
j
>
0
;
j
--
,
kname
++
,
values
++
)
{
mrb_value
k
=
mrb_symbol_value
(
mrb_intern_cstr
(
mrb
,
*
kname
));
if
(
mrb_hash_key_p
(
mrb
,
ksrc
,
k
))
{
*
values
=
mrb_hash_delete_key
(
mrb
,
ksrc
,
k
);
mrb_gc_protect
(
mrb
,
*
values
);
}
else
{
*
values
=
mrb_undef_value
();
}
}
rest
=
kwargs
->
rest
;
}
if
(
rest
)
{
*
rest
=
ksrc
;
}
else
if
(
!
mrb_hash_empty_p
(
mrb
,
ksrc
))
{
ksrc
=
mrb_hash_keys
(
mrb
,
ksrc
);
ksrc
=
RARRAY_PTR
(
ksrc
)[
0
];
mrb_raisef
(
mrb
,
E_ARGUMENT_ERROR
,
"unknown keyword: %v"
,
ksrc
);
}
}
break
;
default:
mrb_raisef
(
mrb
,
E_ARGUMENT_ERROR
,
"invalid argument specifier %c"
,
c
);
break
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment