🐐 First order functions

This commit is contained in:
2026-04-21 18:12:15 +02:00
parent db5b7bf144
commit 693054491f
18 changed files with 407 additions and 46 deletions

158
tests.py
View File

@@ -12,9 +12,9 @@ from fractions import Fraction
from centvrion.ast_nodes import (
ArrayIndex, Bool, BinOp, BuiltIn, DataArray, DataDict, DataRangeArray,
Defini, Continva, Designa, DesignaDestructure, DesignaIndex, DumStatement,
Erumpe, ExpressionStatement, ID, InterpolatedString, Invoca, ModuleCall,
Nullus, Numeral, PerStatement, Program, Redi, SiStatement, String,
UnaryMinus, UnaryNot, Fractio, frac_to_fraction, fraction_to_frac,
Erumpe, ExpressionStatement, Fvnctio, ID, InterpolatedString, Invoca,
ModuleCall, Nullus, Numeral, PerStatement, Program, Redi, SiStatement,
String, UnaryMinus, UnaryNot, Fractio, frac_to_fraction, fraction_to_frac,
num_to_int, int_to_num, make_string,
)
from centvrion.compiler.emitter import compile_program
@@ -2047,5 +2047,157 @@ class TestDictDisplay(unittest.TestCase):
run_test(self, source, nodes, value, output)
# --- First-class functions / FVNCTIO ---
fvnctio_tests = [
# Lambda assigned to variable, then called
(
"DESIGNA f VT FVNCTIO (x) VT { REDI (x + I) }\nINVOCA f (V)",
Program([], [
Designa(ID("f"), Fvnctio([ID("x")], [Redi([BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")])])),
ExpressionStatement(Invoca(ID("f"), [Numeral("V")])),
]),
ValInt(6),
),
# IIFE: immediately invoked lambda
(
"INVOCA FVNCTIO (x) VT { REDI (x * II) } (III)",
Program([], [
ExpressionStatement(Invoca(
Fvnctio([ID("x")], [Redi([BinOp(ID("x"), Numeral("II"), "SYMBOL_TIMES")])]),
[Numeral("III")],
)),
]),
ValInt(6),
),
# Zero-arg lambda
(
"INVOCA FVNCTIO () VT { REDI (XLII) } ()",
Program([], [
ExpressionStatement(Invoca(
Fvnctio([], [Redi([Numeral("XLII")])]),
[],
)),
]),
ValInt(42),
),
# Function passed as argument
(
"DEFINI apply (f, x) VT { REDI (INVOCA f (x)) }\n"
"DESIGNA dbl VT FVNCTIO (n) VT { REDI (n * II) }\n"
"INVOCA apply (dbl, V)",
Program([], [
Defini(ID("apply"), [ID("f"), ID("x")], [
Redi([Invoca(ID("f"), [ID("x")])])
]),
Designa(ID("dbl"), Fvnctio([ID("n")], [
Redi([BinOp(ID("n"), Numeral("II"), "SYMBOL_TIMES")])
])),
ExpressionStatement(Invoca(ID("apply"), [ID("dbl"), Numeral("V")])),
]),
ValInt(10),
),
# Lambda uses caller-scope variable (copy-caller semantics)
(
"DESIGNA n VT III\n"
"DESIGNA f VT FVNCTIO (x) VT { REDI (x + n) }\n"
"INVOCA f (V)",
Program([], [
Designa(ID("n"), Numeral("III")),
Designa(ID("f"), Fvnctio([ID("x")], [
Redi([BinOp(ID("x"), ID("n"), "SYMBOL_PLUS")])
])),
ExpressionStatement(Invoca(ID("f"), [Numeral("V")])),
]),
ValInt(8),
),
# Named function passed as value
(
"DEFINI sqr (x) VT { REDI (x * x) }\n"
"DESIGNA f VT sqr\n"
"INVOCA f (IV)",
Program([], [
Defini(ID("sqr"), [ID("x")], [Redi([BinOp(ID("x"), ID("x"), "SYMBOL_TIMES")])]),
Designa(ID("f"), ID("sqr")),
ExpressionStatement(Invoca(ID("f"), [Numeral("IV")])),
]),
ValInt(16),
),
# Nested lambdas
(
"INVOCA FVNCTIO (x) VT { REDI (INVOCA FVNCTIO (y) VT { REDI (y + I) } (x)) } (V)",
Program([], [
ExpressionStatement(Invoca(
Fvnctio([ID("x")], [
Redi([Invoca(
Fvnctio([ID("y")], [Redi([BinOp(ID("y"), Numeral("I"), "SYMBOL_PLUS")])]),
[ID("x")],
)])
]),
[Numeral("V")],
)),
]),
ValInt(6),
),
# DICE on a function value
(
"DESIGNA f VT FVNCTIO (x) VT { REDI (x) }\nDICE(f)",
Program([], [
Designa(ID("f"), Fvnctio([ID("x")], [Redi([ID("x")])])),
ExpressionStatement(BuiltIn("DICE", [ID("f")])),
]),
ValStr("FVNCTIO"),
"FVNCTIO\n",
),
# Lambda stored in array, called via index
(
"DESIGNA fns VT [FVNCTIO (x) VT { REDI (x + I) }, FVNCTIO (x) VT { REDI (x * II) }]\n"
"INVOCA fns[I] (V)",
Program([], [
Designa(ID("fns"), DataArray([
Fvnctio([ID("x")], [Redi([BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")])]),
Fvnctio([ID("x")], [Redi([BinOp(ID("x"), Numeral("II"), "SYMBOL_TIMES")])]),
])),
ExpressionStatement(Invoca(
ArrayIndex(ID("fns"), Numeral("I")),
[Numeral("V")],
)),
]),
ValInt(6),
),
# Lambda stored in dict, called via key
(
'DESIGNA d VT TABVLA {"add" VT FVNCTIO (x) VT { REDI (x + I) }}\n'
'INVOCA d["add"] (V)',
Program([], [
Designa(ID("d"), DataDict([
(String("add"), Fvnctio([ID("x")], [Redi([BinOp(ID("x"), Numeral("I"), "SYMBOL_PLUS")])])),
])),
ExpressionStatement(Invoca(
ArrayIndex(ID("d"), String("add")),
[Numeral("V")],
)),
]),
ValInt(6),
),
# Multi-param lambda
(
"DESIGNA add VT FVNCTIO (a, b) VT { REDI (a + b) }\nINVOCA add (III, IV)",
Program([], [
Designa(ID("add"), Fvnctio([ID("a"), ID("b")], [
Redi([BinOp(ID("a"), ID("b"), "SYMBOL_PLUS")])
])),
ExpressionStatement(Invoca(ID("add"), [Numeral("III"), Numeral("IV")])),
]),
ValInt(7),
),
]
class TestFvnctio(unittest.TestCase):
@parameterized.expand(fvnctio_tests)
def test_fvnctio(self, source, nodes, value, output=""):
run_test(self, source, nodes, value, output)
if __name__ == "__main__":
unittest.main()