[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[COMMITTED] libpoke,testsuite,doc: support for lambda expressions
From: |
Jose E. Marchesi |
Subject: |
[COMMITTED] libpoke,testsuite,doc: support for lambda expressions |
Date: |
Sun, 08 Nov 2020 13:11:45 +0100 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) |
This commit adds support for lambda expressions to Poke. Lambda
expressions use the following simple syntax:
lambda FUNCTION_SPECIFIER
Where a FUNCTION_SPECIFIER is the same notation that one would use
when defining a function in a `fun' construction. Examples:
(poke) lambda void: {}
#<closure>
(poke) lambda (int i) int: { return i + 2; }
#<closure>
Lambdas can be invoked like any other function value:
(poke) lambda void: {} ()
(poke) lambda (int i) int: { return i + 2; } (10)
12
Lambdas can also be stored in variables:
(poke) var la = lambda (int i) int: { return i + 2; }
(poke) la (10)
12
2020-11-08 Jose E. Marchesi <jemarch@gnu.org>
* libpoke/pkl-lex.l: Support `lambda'.
* libpoke/pkl-tab.y (LABMDA): New token.
(primary): Support lambdas as primaries in expressions.
* libpoke/pkl-ast.h (enum pkl_ast_code): New value PKL_AST_LAMBDA.
(PKL_AST_LAMBDA_FUNCTION): Define.
(struct pkl_ast_lambda): Likewise.
(union pkl_ast_node): New field `lambda'.
(pkl_ast_make_lambda): New prototype.
* libpoke/pkl-ast.c (pkl_ast_make_lambda): New function.
(pkl_ast_node_free): Handle lambda AST nodes.
(pkl_ast_print_1): Likewise.
* libpoke/pkl-pass.c (pkl_do_pass_1): Likewise.
* libpoke/pkl-typify.c (pkl_typify1_ps_lambda): New handler.
(pkl_phase_typify1): Register handler.
* libpoke/pkl-gen.c (pkl_gen_pr_lambda): New handler.
(pkl_gen_ps_lambda): Likewise.
(pkl_phase_gen): Register handlers.
* testsuite/poke.pkl/lambda-1.pk: New test.
* testsuite/poke.pkl/lambda-2.pk: Likewise.
* testsuite/poke.pkl/lambda-3.pk: Likewise.
* testsuite/poke.pkl/lambda-diag-1.pk: Likewise.
* testsuite/Makefile.am (EXTRA_DIST): Add new tests.
* doc/poke.texi (Lambdas): New section.
---
ChangeLog | 26 ++++++++++++++++++
doc/poke.texi | 28 ++++++++++++++++++++
libpoke/pkl-ast.c | 25 ++++++++++++++++++
libpoke/pkl-ast.h | 18 +++++++++++++
libpoke/pkl-gen.c | 41 +++++++++++++++++++++++++++++
libpoke/pkl-lex.l | 1 +
libpoke/pkl-pass.c | 3 +++
libpoke/pkl-tab.y | 16 +++++++++++
libpoke/pkl-typify.c | 12 +++++++++
testsuite/Makefile.am | 4 +++
testsuite/poke.pkl/lambda-1.pk | 6 +++++
testsuite/poke.pkl/lambda-2.pk | 6 +++++
testsuite/poke.pkl/lambda-3.pk | 5 ++++
testsuite/poke.pkl/lambda-diag-1.pk | 3 +++
14 files changed, 194 insertions(+)
create mode 100644 testsuite/poke.pkl/lambda-1.pk
create mode 100644 testsuite/poke.pkl/lambda-2.pk
create mode 100644 testsuite/poke.pkl/lambda-3.pk
create mode 100644 testsuite/poke.pkl/lambda-diag-1.pk
diff --git a/ChangeLog b/ChangeLog
index fa4b5137..ef1cf618 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,29 @@
+2020-11-08 Jose E. Marchesi <jemarch@gnu.org>
+
+ * libpoke/pkl-lex.l: Support `lambda'.
+ * libpoke/pkl-tab.y (LABMDA): New token.
+ (primary): Support lambdas as primaries in expressions.
+ * libpoke/pkl-ast.h (enum pkl_ast_code): New value PKL_AST_LAMBDA.
+ (PKL_AST_LAMBDA_FUNCTION): Define.
+ (struct pkl_ast_lambda): Likewise.
+ (union pkl_ast_node): New field `lambda'.
+ (pkl_ast_make_lambda): New prototype.
+ * libpoke/pkl-ast.c (pkl_ast_make_lambda): New function.
+ (pkl_ast_node_free): Handle lambda AST nodes.
+ (pkl_ast_print_1): Likewise.
+ * libpoke/pkl-pass.c (pkl_do_pass_1): Likewise.
+ * libpoke/pkl-typify.c (pkl_typify1_ps_lambda): New handler.
+ (pkl_phase_typify1): Register handler.
+ * libpoke/pkl-gen.c (pkl_gen_pr_lambda): New handler.
+ (pkl_gen_ps_lambda): Likewise.
+ (pkl_phase_gen): Register handlers.
+ * testsuite/poke.pkl/lambda-1.pk: New test.
+ * testsuite/poke.pkl/lambda-2.pk: Likewise.
+ * testsuite/poke.pkl/lambda-3.pk: Likewise.
+ * testsuite/poke.pkl/lambda-diag-1.pk: Likewise.
+ * testsuite/Makefile.am (EXTRA_DIST): Add new tests.
+ * doc/poke.texi (Lambdas): New section.
+
2020-11-08 Jose E. Marchesi <jemarch@gnu.org>
* etc/poke-mode.el (poke-font-lock-keywords): Remove unneeded
diff --git a/doc/poke.texi b/doc/poke.texi
index ac0b0ac1..fd5b42e7 100644
--- a/doc/poke.texi
+++ b/doc/poke.texi
@@ -7678,6 +7678,7 @@ The value computed by the expression will be discarded.
* Variadic Functions:: Functions taking any number of arguments.
* Calling Functions:: Invoking functions.
* Function Types:: Useful for defining interfaces.
+* Lambdas:: Functions in expressions.
@end menu
@node Function Declarations
@@ -7862,6 +7863,33 @@ fun printf (string fmt, args@dots{}) void: @{ @dots{} @}
@noindent
is @code{(string,@dots{})void:}.
+@node Lambdas
+@subsection Lambdas
+
+Poke support @dfn{lambda expressions} using the following syntax:
+
+@example
+lambda @var{function_specifier}
+@end example
+
+@noindent
+Where @var{function_specifier} is any function specifier. Examples:
+
+@example
+lambda void: @{@}
+lambda (int i) int: @{ return i * 2; @}
+@end example
+
+@noindent
+Lambdas can be manipulated exactly like any other Poke value, and can
+be stored in variables. Therefore, this is an alternative way of
+defining a named function (which can't be recursive for obvious
+reasons):
+
+@example
+var double = lambda (int i) int: @{ return i * 2 @};
+@end example
+
@node Endianness
@section Endianness
@cindex endianness
diff --git a/libpoke/pkl-ast.c b/libpoke/pkl-ast.c
index 9b210b22..9615adcc 100644
--- a/libpoke/pkl-ast.c
+++ b/libpoke/pkl-ast.c
@@ -1538,6 +1538,19 @@ pkl_ast_make_var (pkl_ast ast, pkl_ast_node name,
return var;
}
+/* Build and return an AST node for a lambda expression. */
+
+pkl_ast_node
+pkl_ast_make_lambda (pkl_ast ast, pkl_ast_node function)
+{
+ pkl_ast_node lambda = pkl_ast_make_node (ast, PKL_AST_LAMBDA);
+
+ assert (function);
+
+ PKL_AST_LAMBDA_FUNCTION (lambda) = ASTREF (function);
+ return lambda;
+}
+
/* Build and return an AST node for a compound statement. */
pkl_ast_node
@@ -2067,6 +2080,11 @@ pkl_ast_node_free (pkl_ast_node ast)
pkl_ast_node_free (PKL_AST_VAR_DECL (ast));
break;
+ case PKL_AST_LAMBDA:
+
+ pkl_ast_node_free (PKL_AST_LAMBDA_FUNCTION (ast));
+ break;
+
case PKL_AST_COMP_STMT:
for (t = PKL_AST_COMP_STMT_STMTS (ast); t; t = n)
@@ -2842,6 +2860,13 @@ pkl_ast_print_1 (FILE *fp, pkl_ast_node ast, int indent)
PRINT_AST_IMM (over, VAR_OVER, "%d");
break;
+ case PKL_AST_LAMBDA:
+ IPRINTF ("LAMBDA::\n");
+
+ PRINT_COMMON_FIELDS;
+ PRINT_AST_SUBAST (function, LAMBDA_FUNCTION);
+ break;
+
case PKL_AST_COMP_STMT:
IPRINTF ("COMP_STMT::\n");
diff --git a/libpoke/pkl-ast.h b/libpoke/pkl-ast.h
index d56d80b3..6a44cfb7 100644
--- a/libpoke/pkl-ast.h
+++ b/libpoke/pkl-ast.h
@@ -55,6 +55,7 @@ enum pkl_ast_code
PKL_AST_FUNCALL,
PKL_AST_FUNCALL_ARG,
PKL_AST_VAR,
+ PKL_AST_LAMBDA,
PKL_AST_GCD,
PKL_AST_LAST_EXP = PKL_AST_GCD,
/* Types. */
@@ -1283,6 +1284,22 @@ pkl_ast_node pkl_ast_make_var (pkl_ast ast,
pkl_ast_node initial,
int back, int over);
+/* PKL_AST_LAMBDA nodes represent lambda expressions, which evaluate
+ to a function.
+
+ FUNCTION is a node of type PKL_AST_FUNC. */
+
+#define PKL_AST_LAMBDA_FUNCTION(AST) ((AST)->lambda.function)
+
+struct pkl_ast_lambda
+{
+ struct pkl_ast_common common;
+
+ union pkl_ast_node *function;
+};
+
+pkl_ast_node pkl_ast_make_lambda (pkl_ast ast, pkl_ast_node function);
+
/* PKL_AST_COMPOUND_STMT nodes represent compound statements in the
language.
@@ -1715,6 +1732,7 @@ union pkl_ast_node
struct pkl_ast_funcall funcall;
struct pkl_ast_funcall_arg funcall_arg;
struct pkl_ast_var var;
+ struct pkl_ast_lambda lambda;
/* Types. */
struct pkl_ast_type type;
struct pkl_ast_struct_type_field sct_type_elem;
diff --git a/libpoke/pkl-gen.c b/libpoke/pkl-gen.c
index 65ea7735..87762621 100644
--- a/libpoke/pkl-gen.c
+++ b/libpoke/pkl-gen.c
@@ -444,6 +444,45 @@ PKL_PHASE_BEGIN_HANDLER (pkl_gen_ps_var)
}
PKL_PHASE_END_HANDLER
+/*
+ * LAMBDA
+ * | FUNCTION
+ */
+
+PKL_PHASE_BEGIN_HANDLER (pkl_gen_pr_lambda)
+{
+ /* FUNCTION is a PKL_AST_FUNC, that will compile into a program
+ containing the function code. Push a new assembler. */
+ PKL_GEN_PUSH_ASM (pkl_asm_new (PKL_PASS_AST,
+ PKL_GEN_PAYLOAD->compiler,
+ 0 /* prologue */));
+}
+PKL_PHASE_END_HANDLER
+
+/*
+ * | FUNCTION
+ * LAMBDA
+ */
+
+PKL_PHASE_BEGIN_HANDLER (pkl_gen_ps_lambda)
+{
+ /* At this point the code for FUNCTION has been assembled in the
+ current macroassembler. Finalize the program and put it in a PVM
+ closure, along with the current environment. */
+
+ pvm_program program = pkl_asm_finish (PKL_GEN_ASM,
+ 0 /* epilogue */);
+ pvm_val closure;
+
+ PKL_GEN_POP_ASM;
+ pvm_program_make_executable (program);
+ closure = pvm_make_cls (program);
+
+ pkl_asm_insn (PKL_GEN_ASM, PKL_INSN_PUSH, closure);
+ pkl_asm_insn (PKL_GEN_ASM, PKL_INSN_PEC);
+}
+PKL_PHASE_END_HANDLER
+
/*
* NULL_STMT
*/
@@ -3433,6 +3472,8 @@ struct pkl_phase pkl_phase_gen
PKL_PHASE_PR_HANDLER (PKL_AST_DECL, pkl_gen_pr_decl),
PKL_PHASE_PS_HANDLER (PKL_AST_DECL, pkl_gen_ps_decl),
PKL_PHASE_PS_HANDLER (PKL_AST_VAR, pkl_gen_ps_var),
+ PKL_PHASE_PR_HANDLER (PKL_AST_LAMBDA, pkl_gen_pr_lambda),
+ PKL_PHASE_PS_HANDLER (PKL_AST_LAMBDA, pkl_gen_ps_lambda),
PKL_PHASE_PR_HANDLER (PKL_AST_COND_EXP, pkl_gen_pr_cond_exp),
PKL_PHASE_PR_HANDLER (PKL_AST_COMP_STMT, pkl_gen_pr_comp_stmt),
PKL_PHASE_PS_HANDLER (PKL_AST_COMP_STMT, pkl_gen_ps_comp_stmt),
diff --git a/libpoke/pkl-lex.l b/libpoke/pkl-lex.l
index 00f2b38c..92ba1468 100644
--- a/libpoke/pkl-lex.l
+++ b/libpoke/pkl-lex.l
@@ -224,6 +224,7 @@ S ::
"big" { return BIG; }
"little" { return LITTLE; }
"load" { return LOAD; }
+"lambda" { return LAMBDA; }
"__PKL_BUILTIN_RAND__" {
if (yyextra->bootstrapped) REJECT; return BUILTIN_RAND; }
"__PKL_BUILTIN_GET_ENDIAN__" {
diff --git a/libpoke/pkl-pass.c b/libpoke/pkl-pass.c
index ac85f095..e464fe2e 100644
--- a/libpoke/pkl-pass.c
+++ b/libpoke/pkl-pass.c
@@ -567,6 +567,9 @@ pkl_do_pass_1 (pkl_compiler compiler,
if (PKL_AST_PRINT_STMT_ARGS (node))
PKL_PASS_CHAIN (PKL_AST_PRINT_STMT_ARGS (node));
break;
+ case PKL_AST_LAMBDA:
+ PKL_PASS (PKL_AST_LAMBDA_FUNCTION (node));
+ break;
case PKL_AST_NULL_STMT:
case PKL_AST_INTEGER:
case PKL_AST_STRING:
diff --git a/libpoke/pkl-tab.y b/libpoke/pkl-tab.y
index 131134ec..c4a5ea8c 100644
--- a/libpoke/pkl-tab.y
+++ b/libpoke/pkl-tab.y
@@ -335,6 +335,7 @@ token <integer> UNION _("keyword `union'")
%token PRINT _("keyword `print'")
%token PRINTF _("keyword `printf'")
%token LOAD _("keyword `load'")
+%token LAMBDA _("keyword lambda")
%token BUILTIN_RAND BUILTIN_GET_ENDIAN BUILTIN_SET_ENDIAN
%token BUILTIN_GET_IOS BUILTIN_SET_IOS BUILTIN_OPEN BUILTIN_CLOSE
%token BUILTIN_IOSIZE BUILTIN_GETENV BUILTIN_FORGET
@@ -971,6 +972,21 @@ primary:
{
$$ = $2;
}
+ | LAMBDA
+ {
+ /* function_specifier needs to know whether we are
+ in a function declaration or a method
+ declaration. */
+ pkl_parser->in_method_decl_p = 0;
+ }
+ function_specifier
+ {
+ /* Annotate the contained RETURN statements with
+ their function and their lexical nest level
+ within the function. */
+ pkl_ast_finish_returns ($3);
+ $$ = pkl_ast_make_lambda (pkl_parser->ast, $3);
+ }
;
funcall:
diff --git a/libpoke/pkl-typify.c b/libpoke/pkl-typify.c
index 493abddc..5e190f72 100644
--- a/libpoke/pkl-typify.c
+++ b/libpoke/pkl-typify.c
@@ -2021,6 +2021,17 @@ PKL_PHASE_BEGIN_HANDLER (pkl_typify1_ps_var)
}
PKL_PHASE_END_HANDLER
+/* The type of a lambda node is the type of its function. */
+
+PKL_PHASE_BEGIN_HANDLER (pkl_typify1_ps_lambda)
+{
+ pkl_ast_node lambda = PKL_PASS_NODE;
+ pkl_ast_node function = PKL_AST_LAMBDA_FUNCTION (lambda);
+
+ PKL_AST_TYPE (lambda) = ASTREF (PKL_AST_TYPE (function));
+}
+PKL_PHASE_END_HANDLER
+
/* The type of the condition of a loop statement should be a boolean. */
PKL_PHASE_BEGIN_HANDLER (pkl_typify1_ps_loop_stmt)
@@ -2758,6 +2769,7 @@ struct pkl_phase pkl_phase_typify1
{
PKL_PHASE_PR_HANDLER (PKL_AST_PROGRAM, pkl_typify_pr_program),
PKL_PHASE_PS_HANDLER (PKL_AST_VAR, pkl_typify1_ps_var),
+ PKL_PHASE_PS_HANDLER (PKL_AST_LAMBDA, pkl_typify1_ps_lambda),
PKL_PHASE_PS_HANDLER (PKL_AST_CAST, pkl_typify1_ps_cast),
PKL_PHASE_PS_HANDLER (PKL_AST_ISA, pkl_typify1_ps_isa),
PKL_PHASE_PS_HANDLER (PKL_AST_MAP, pkl_typify1_ps_map),
diff --git a/testsuite/Makefile.am b/testsuite/Makefile.am
index cf936d19..1656dbd5 100644
--- a/testsuite/Makefile.am
+++ b/testsuite/Makefile.am
@@ -957,6 +957,10 @@ EXTRA_DIST = \
poke.pkl/isa-7.pk \
poke.pkl/isa-8.pk \
poke.pkl/isa-9.pk \
+ poke.pkl/lambda-1.pk \
+ poke.pkl/lambda-2.pk \
+ poke.pkl/lambda-3.pk \
+ poke.pkl/lambda-diag-1.pk \
poke.pkl/le-arrays-diag-1.pk \
poke.pkl/le-integers-1.pk \
poke.pkl/le-integers-2.pk \
diff --git a/testsuite/poke.pkl/lambda-1.pk b/testsuite/poke.pkl/lambda-1.pk
new file mode 100644
index 00000000..5e33ab2d
--- /dev/null
+++ b/testsuite/poke.pkl/lambda-1.pk
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+
+/* { dg-command "lambda void: {}" } */
+/* { dg-output "#<closure>" } */
+/* { dg-command "lambda void: {} ()" } */
+/* { dg-output "\n" } */
diff --git a/testsuite/poke.pkl/lambda-2.pk b/testsuite/poke.pkl/lambda-2.pk
new file mode 100644
index 00000000..8c9aacfc
--- /dev/null
+++ b/testsuite/poke.pkl/lambda-2.pk
@@ -0,0 +1,6 @@
+/* { dg-do run } */
+
+/* { dg-command "lambda (int i) int: { return i + 2; }" } */
+/* { dg-output "#<closure>" } */
+/* { dg-command "lambda (int i) int: { return i + 2; } (10)" } */
+/* { dg-output "\n12" } */
diff --git a/testsuite/poke.pkl/lambda-3.pk b/testsuite/poke.pkl/lambda-3.pk
new file mode 100644
index 00000000..b505330d
--- /dev/null
+++ b/testsuite/poke.pkl/lambda-3.pk
@@ -0,0 +1,5 @@
+/* { dg-do run } */
+
+/* { dg-command "var la = lambda (int i) int: { return i + 2; }" } */
+/* { dg-command "la (12)" } */
+/* { dg-output "14" } */
diff --git a/testsuite/poke.pkl/lambda-diag-1.pk
b/testsuite/poke.pkl/lambda-diag-1.pk
new file mode 100644
index 00000000..9d3ce0c5
--- /dev/null
+++ b/testsuite/poke.pkl/lambda-diag-1.pk
@@ -0,0 +1,3 @@
+/* { dg-do compile } */
+
+lambda (int i, string s) void: {} (10, 20); /* { dg-error ".*\n.*expected
string" } */
--
2.25.0.2.g232378479e
- [COMMITTED] libpoke,testsuite,doc: support for lambda expressions,
Jose E. Marchesi <=