diff --git a/docs/OPERATORS.md b/docs/OPERATORS.md index c748fbb..72a1b1e 100644 --- a/docs/OPERATORS.md +++ b/docs/OPERATORS.md @@ -27,9 +27,9 @@ This is clearly unacceptable. > macro definition and make sure those names are looked up in the scope > of the macro definition instead of the scope where the macro was used. -This offers hope, if we can re-work the macro system to be hygenic by default, -then the parser instead of generating -`addition(a, b)` for `a + b` could instead generate: +This offers hope, if we can re-work the macro system to be hygenic by +default, then the parser instead of generating `addition(a, b)` for `a + +b` could instead generate: ``` macro gensym$1(a, b) { addition(a, b) } @@ -38,21 +38,23 @@ macro gensym$1(a, b) { addition(a, b) } at the point of the operator declaration, and generate `gensym$1(a, b)` when `a + b` is subsequently encountered. -Firstly I now think the use of a `$` prefix to indicate a gensym in a macro -is not the best idea. Instead the lambda conversion should identify bound -`let` variables and replace them automatically. That also frees up `$` as a -potentially useful user-defined prefix operator. +Firstly I now think the use of a `$` prefix to indicate a gensym in a +macro is not the best idea. Instead the lambda conversion should identify +bound `let` variables and replace them automatically. That also frees up +`$` as a potentially useful user-defined prefix operator. -The bigger problem is that we can't continue to do naiive macro expansion during -the lambda conversion step, or we'd be back where we started with -`addition(a, b)` referring to whatever `addition` happens to be the current -definition. +The bigger problem is that we can't continue to do naiive macro expansion +during the lambda conversion step, or we'd be back where we started with +`addition(a, b)` referring to whatever `addition` happens to be the +current definition. -We may have to revert to the scheme definition of a macro: pass the arguments -unevaluated to the macro, evaluate the macro body, then re-evaluate the result. +We may have to revert to the scheme definition of a macro: pass the +arguments unevaluated to the macro, evaluate the macro body, then +re-evaluate the result. -But we really don't want to have the macro evaluated like that, because F♮ is not -homoiconic, "evaluating the macro body" can only mean substitution. +But we really don't want to have the macro evaluated like that, +because F♮ is not homoiconic, "evaluating the macro body" can only +mean substitution. What if the arguments to macros were wrapped in a closure? @@ -62,16 +64,18 @@ macro AND(a, b) { if (a) { b } else { false } } => fn AND(a, b) { if (a()) { b() AND(a, b) => AND(fn () { a }, fn () { b }) ``` -That would definately work, though it won't be quite as efficient. It solves both -local scoping rules, since `AND` is now a normal function then free variables in the -body are evaluated in the context of the function definition, and variables in the -argument expressions are evaluated in the calling context. +That would definately work, though it won't be quite as efficient. It +solves both local scoping rules, since `AND` is now a normal function then +free variables in the body are evaluated in the context of the function +definition, and variables in the argument expressions are evaluated in +the calling context. -Got that working, and we're also handling local shadowing of arguments so they don't -get wrapped in an invocation unless they are the same lexical variable. +> Got that working, and we're also handling local shadowing of arguments +so they don't get wrapped in an invocation unless they are the same +lexical variable. -One little unnecssary inefficiency needs to be addressed. If one macro calls another, -for example +One little unnecssary inefficiency needs to be addressed. If one macro +calls another, for example ``` macro NAND(a, b) { NOT(AND(a, b)) } @@ -109,6 +113,6 @@ pattern if the argument is being modified, for example if a macro called another with it's argument modified in some way then the pattern i.e. `fn() { a() + 1 }` would be necessary. -Got option 1 working, but no need for extra types, just inspect the +> Got option 1 working, but no need for extra types, just inspect the thunk during macro conversion, if it has no arguments and just contains a symbol that would otherwise be invoked then return the symbol. diff --git a/docs/generated/ast.md b/docs/generated/ast.md index a2474a6..f83361f 100644 --- a/docs/generated/ast.md +++ b/docs/generated/ast.md @@ -18,8 +18,6 @@ AstDefinitions --definition--> AstDefinition AstDefinitions --next--> AstDefinitions AstDefine --symbol--> HashSymbol AstDefine --expression--> AstExpression -AstGensymDefine --basename--> HashSymbol -AstGensymDefine --expression--> AstExpression AstAlias --name--> HashSymbol AstAlias --type--> AstType AstExprAlias --name--> HashSymbol @@ -89,7 +87,6 @@ AstTypeConstructorArgs --map--> AstTypeMap AstLookupOrSymbol --symbol--> HashSymbol AstLookupOrSymbol --lookup--> AstLookupSymbol AstDefinition --define--> AstDefine -AstDefinition --gensymDefine--> AstGensymDefine AstDefinition --typeDef--> AstTypeDef AstDefinition --macro--> AstDefMacro AstDefinition --alias--> AstAlias @@ -114,7 +111,6 @@ AstExpression --alias--> AstExprAlias AstExpression --funCall--> AstFunCall AstExpression --lookup--> AstLookup AstExpression --symbol--> HashSymbol -AstExpression --gensym--> HashSymbol AstExpression --number--> MaybeBigInt AstExpression --character--> character AstExpression --fun--> AstCompositeFunction diff --git a/docs/generated/lambda.md b/docs/generated/lambda.md index d180397..90e92d0 100644 --- a/docs/generated/lambda.md +++ b/docs/generated/lambda.md @@ -5,15 +5,12 @@ Plain lambda structures generated by lambda conversion. ```mermaid flowchart TD LamMacroTable --entries--> entries -LamGenSymTable --entries--> entries +LamMacroArgsTable --entries--> entries LamInfoTable --entries--> entries LamAliasTable --entries--> entries LamExpTable --entries--> entries LamLam --args--> LamVarList LamLam --exp--> LamExp -LamMacro --args--> LamVarList -LamMacro --exp--> LamExp -LamMacro --env--> LamContext LamVarList --var--> HashSymbol LamVarList --next--> LamVarList LamPrimApp --type--> LamPrimOp @@ -74,7 +71,6 @@ LamLetRec --nbindings--> int LamLetRec --bindings--> LamLetRecBindings LamLetRec --body--> LamExp LamLetRecBindings --var--> HashSymbol -LamLetRecBindings --isGenSym--> bool LamLetRecBindings --val--> LamExp LamLetRecBindings --next--> LamLetRecBindings LamContext --frame--> LamInfoTable @@ -117,7 +113,6 @@ LamTypeConstructorInfo --index--> int LamExp --namespaces--> LamNamespaceArray LamExp --lam--> LamLam LamExp --var--> HashSymbol -LamExp --gensym--> HashSymbol LamExp --stdint--> int LamExp --biginteger--> MaybeBigInt LamExp --prim--> LamPrimApp diff --git a/docs/generated/pratt.md b/docs/generated/pratt.md index e7b09ea..a77da67 100644 --- a/docs/generated/pratt.md +++ b/docs/generated/pratt.md @@ -31,6 +31,7 @@ PrattParser --namespaces--> PrattIntTable PrattParser --lexer--> PrattLexer PrattParser --trie--> PrattTrie PrattParser --panicMode--> bool +PrattParser --isPreamble--> bool PrattParser --next--> PrattParser PrattRecord --symbol--> HashSymbol PrattRecord --prefixOp--> PrattOp @@ -50,6 +51,7 @@ PrattNumberState["enum PrattNumberState"] PrattStringState["enum PrattStringState"] PrattFixity["enum PrattFixity"] PrattUTF8["PrattUTF8[]"] --entries--> uchar +PrattParsers["PrattParsers[]"] --entries--> PrattParser PrattUnicode["PrattUnicode[]"] --entries--> character PrattValueVal PrattValueType diff --git a/src/lambda_simplfication.c b/src/lambda_simplfication.c new file mode 100644 index 0000000..025aa9c --- /dev/null +++ b/src/lambda_simplfication.c @@ -0,0 +1,359 @@ +/* + * CEKF - VM supporting amb + * Copyright (C) 2022-2024 Bill Hails + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include "common.h" +#include "lambda_simplification.h" +#include "symbol.h" +#include "lambda_pp.h" +#include "lambda_debug.h" + +#ifdef DEBUG_LAMBDA_SIMPLIFICATION +# include "debugging_on.h" +#else +# include "debugging_off.h" +#endif + +// fn () { a() } == a +static LamExp *performLamSimplifications(LamLam *lam) { + ENTER(performLamSimplifications); + lam->exp = lamPerformSimplifications(lam->exp); + if ( lam->args == NULL + && lam->exp->type == LAMEXP_TYPE_APPLY + && lam->exp->val.apply->args == NULL) { + LEAVE(performLamSimplifications); + return lam->exp->val.apply->function; + } + LEAVE(performLamSimplifications); + return newLamExp_Lam(CPI(lam), lam); +} + +static HashSymbol *performVarSimplifications(HashSymbol *var) { + return var; +} + +static LamPrimApp *performPrimSimplifications(LamPrimApp *prim) { + ENTER(performPrimSimplifications); + prim->exp1 = lamPerformSimplifications(prim->exp1); + prim->exp2 = lamPerformSimplifications(prim->exp2); + LEAVE(performPrimSimplifications); + return prim; +} + +static LamUnaryApp *performUnarySimplifications(LamUnaryApp *unary) { + ENTER(performUnarySimplifications); + unary->exp = lamPerformSimplifications(unary->exp); + LEAVE(performUnarySimplifications); + return unary; +} + +static LamSequence *performSequenceSimplifications(LamSequence *sequence) { + ENTER(performSequenceSimplifications); + if (sequence == NULL) { + LEAVE(performSequenceSimplifications); + return NULL; + } + sequence->next = + performSequenceSimplifications(sequence->next); + sequence->exp = lamPerformSimplifications(sequence->exp); + LEAVE(performSequenceSimplifications); + return sequence; +} + +static LamList *performListSimplifications(LamList *list) { + ENTER(performListSimplifications); + if (list == NULL) { + LEAVE(performListSimplifications); + return NULL; + } + list->next = performListSimplifications(list->next); + list->exp = lamPerformSimplifications(list->exp); + LEAVE(performListSimplifications); + return list; +} + +static LamTupleIndex *performTupleIndexSimplifications(LamTupleIndex *tupleIndex) { + tupleIndex->exp = lamPerformSimplifications(tupleIndex->exp); + return tupleIndex; +} + +static LamPrint *performPrintSimplifications(LamPrint *print) { + print->exp = lamPerformSimplifications(print->exp); + print->printer = lamPerformSimplifications(print->printer); + return print; +} + +static LamLookup *performLookupSimplifications(LamLookup *lookup) { + lookup->exp = lamPerformSimplifications(lookup->exp); + return lookup; +} + +static LamMakeVec *performMakeVecSimplifications(LamMakeVec *makeVec) { + ENTER(performMakeVecSimplifications); + makeVec->args = performListSimplifications(makeVec->args); + LEAVE(performMakeVecSimplifications); + return makeVec; +} + +static LamDeconstruct *performDeconstructSimplifications(LamDeconstruct *deconstruct) { + ENTER(performDeconstructSimplifications); + deconstruct->exp = lamPerformSimplifications(deconstruct->exp); + LEAVE(performDeconstructSimplifications); + return deconstruct; +} + +static LamConstruct *performConstructSimplifications(LamConstruct *construct) { + ENTER(performConstructSimplifications); + construct->args = performListSimplifications(construct->args); + LEAVE(performConstructSimplifications); + return construct; +} + +static LamApply *performApplySimplifications(LamApply *apply) { + ENTER(performApplySimplifications); + apply->function = lamPerformSimplifications(apply->function); + apply->args = performListSimplifications(apply->args); + LEAVE(performApplySimplifications); + return apply; +} + +static LamIff *performIffSimplifications(LamIff *iff) { + ENTER(performIffSimplifications); + iff->condition = lamPerformSimplifications(iff->condition); + iff->consequent = lamPerformSimplifications(iff->consequent); + iff->alternative = lamPerformSimplifications(iff->alternative); + LEAVE(performIffSimplifications); + return iff; +} + +static LamLetRecBindings *performBindingsSimplifications(LamLetRecBindings *bindings) { + ENTER(performBindingsSimplifications); + if (bindings == NULL) { + LEAVE(performBindingsSimplifications); + return NULL; + } + bindings->next = performBindingsSimplifications(bindings->next); + bindings->val = lamPerformSimplifications(bindings->val); + LEAVE(performBindingsSimplifications); + return bindings; +} + +static LamLet *performLetSimplifications(LamLet *let) { + ENTER(performLetSimplifications); + let->value = lamPerformSimplifications(let->value); + let->body = lamPerformSimplifications(let->body); + LEAVE(performLetSimplifications); + return let; +} + +static LamLetRec *performLetRecSimplifications(LamLetRec *letrec) { + ENTER(performLetRecSimplifications); + letrec->bindings = performBindingsSimplifications(letrec->bindings); + letrec->body = lamPerformSimplifications(letrec->body); + LEAVE(performLetRecSimplifications); + return letrec; +} + +static LamTypeDefs *performTypeDefsSimplifications(LamTypeDefs *typedefs) { + ENTER(performTypeDefsSimplifications); + typedefs->body = lamPerformSimplifications(typedefs->body); + LEAVE(performTypeDefsSimplifications); + return typedefs; +} + +static LamMatchList *performCaseSimplifications(LamMatchList *cases) { + ENTER(performCaseSimplifications); + if (cases == NULL) { + LEAVE(performCaseSimplifications); + return NULL; + } + cases->next = performCaseSimplifications(cases->next); + cases->body = lamPerformSimplifications(cases->body); + LEAVE(performCaseSimplifications); + return cases; +} + +static LamMatch *performMatchSimplifications(LamMatch *match) { + ENTER(performMatchSimplifications); + match->index = lamPerformSimplifications(match->index); + match->cases = performCaseSimplifications(match->cases); + LEAVE(performMatchSimplifications); + return match; +} + +static LamAmb *performAmbSimplifications(LamAmb *amb) { + ENTER(performAmbSimplifications); + amb->left = lamPerformSimplifications(amb->left); + amb->right = lamPerformSimplifications(amb->right); + LEAVE(performAmbSimplifications); + return amb; +} + +static LamIntCondCases *performIntCondCaseSimplifications(LamIntCondCases *cases) { + ENTER(performIntCondCaseSimplifications); + if (cases == NULL) { + LEAVE(performIntCondCaseSimplifications); + return NULL; + } + cases->body = lamPerformSimplifications(cases->body); + cases->next = performIntCondCaseSimplifications(cases->next); + LEAVE(performIntCondCaseSimplifications); + return cases; +} + +static LamCharCondCases *performCharCondCaseSimplifications(LamCharCondCases *cases) { + ENTER(performCharCondCaseSimplifications); + if (cases == NULL) { + LEAVE(performCharCondCaseSimplifications); + return NULL; + } + cases->body = lamPerformSimplifications(cases->body); + cases->next = performCharCondCaseSimplifications(cases->next); + LEAVE(performCharCondCaseSimplifications); + return cases; +} + +static LamCondCases *performCondCaseSimplifications(LamCondCases *cases) { + ENTER(performCondCaseSimplifications); + if (cases == NULL) { + LEAVE(performCondCaseSimplifications); + return NULL; + } + switch (cases->type) { + case LAMCONDCASES_TYPE_INTEGERS: + cases->val.integers = performIntCondCaseSimplifications(cases->val.integers); + break; + case LAMCONDCASES_TYPE_CHARACTERS: + cases->val.characters = performCharCondCaseSimplifications(cases->val.characters); + break; + default: + cant_happen("unrecognised %s", lamCondCasesTypeName(cases->type)); + } + LEAVE(performCondCaseSimplifications); + return cases; +} + +static LamCond *performCondSimplifications(LamCond *cond) { + ENTER(performCondSimplifications); + cond->value = lamPerformSimplifications(cond->value); + cond->cases = performCondCaseSimplifications(cond->cases); + LEAVE(performCondSimplifications); + return cond; +} + +static LamNamespaceArray *performNamespacesSimplifications(LamNamespaceArray *namespaces) { + for (Index i = 0; i < namespaces->size; i++) { + namespaces->entries[i] = lamPerformSimplifications(namespaces->entries[i]); + } + return namespaces; +} + +LamExp *lamPerformSimplifications(LamExp *exp) { + ENTER(lamPerformSimplifications); + // ppLamExp(exp); + // eprintf("\n"); + if (exp != NULL) { + switch (exp->type) { + case LAMEXP_TYPE_BIGINTEGER: + case LAMEXP_TYPE_STDINT: + case LAMEXP_TYPE_CHARACTER: + case LAMEXP_TYPE_BACK: + case LAMEXP_TYPE_COND_DEFAULT: + case LAMEXP_TYPE_ERROR: + case LAMEXP_TYPE_CONSTANT: + case LAMEXP_TYPE_CONSTRUCTOR: + case LAMEXP_TYPE_ENV: + break; + case LAMEXP_TYPE_LAM: + exp = performLamSimplifications(exp->val.lam); + break; + case LAMEXP_TYPE_VAR: + exp->val.var = performVarSimplifications(exp->val.var); + break; + case LAMEXP_TYPE_PRIM: + exp->val.prim = performPrimSimplifications(exp->val.prim); + break; + case LAMEXP_TYPE_UNARY: + exp->val.unary = performUnarySimplifications(exp->val.unary); + break; + case LAMEXP_TYPE_LIST: + exp->val.list = performSequenceSimplifications(exp->val.list); + break; + case LAMEXP_TYPE_MAKEVEC: + exp->val.makeVec = performMakeVecSimplifications(exp->val.makeVec); + break; + case LAMEXP_TYPE_DECONSTRUCT: + exp->val.deconstruct = performDeconstructSimplifications(exp->val.deconstruct); + break; + case LAMEXP_TYPE_CONSTRUCT: + exp->val.construct = performConstructSimplifications(exp->val.construct); + break; + case LAMEXP_TYPE_TAG: + exp->val.tag = lamPerformSimplifications(exp->val.tag); + break; + case LAMEXP_TYPE_APPLY: + exp->val.apply = performApplySimplifications(exp->val.apply); + break; + case LAMEXP_TYPE_IFF: + exp->val.iff = performIffSimplifications(exp->val.iff); + break; + case LAMEXP_TYPE_COND: + exp->val.cond = performCondSimplifications(exp->val.cond); + break; + case LAMEXP_TYPE_CALLCC: + exp->val.callcc = lamPerformSimplifications(exp->val.callcc); + break; + case LAMEXP_TYPE_LET: + exp->val.let = performLetSimplifications(exp->val.let); + break; + case LAMEXP_TYPE_LETREC: + exp->val.letrec = performLetRecSimplifications(exp->val.letrec); + break; + case LAMEXP_TYPE_TYPEDEFS: + exp->val.typedefs = performTypeDefsSimplifications(exp->val.typedefs); + break; + case LAMEXP_TYPE_MATCH: + exp->val.match = performMatchSimplifications(exp->val.match); + break; + case LAMEXP_TYPE_AMB: + exp->val.amb = performAmbSimplifications(exp->val.amb); + break; + case LAMEXP_TYPE_MAKE_TUPLE: + exp->val.make_tuple = performListSimplifications(exp->val.make_tuple); + break; + case LAMEXP_TYPE_TUPLE_INDEX: + exp->val.tuple_index = performTupleIndexSimplifications(exp->val.tuple_index); + break; + case LAMEXP_TYPE_PRINT: + exp->val.print = performPrintSimplifications(exp->val.print); + break; + case LAMEXP_TYPE_LOOKUP: + exp->val.lookup = performLookupSimplifications(exp->val.lookup); + break; + case LAMEXP_TYPE_NAMESPACES: + exp->val.namespaces = performNamespacesSimplifications(exp->val.namespaces); + break; + default: + cant_happen("unrecognized %s", lamExpTypeName(exp->type)); + } + } + LEAVE(lamPerformSimplifications); + return exp; +} diff --git a/src/lambda_simplification.h b/src/lambda_simplification.h new file mode 100644 index 0000000..2136266 --- /dev/null +++ b/src/lambda_simplification.h @@ -0,0 +1,25 @@ +#ifndef cekf_lambda_simplification_h +# define cekf_lambda_simplification_h +/* + * CEKF - VM supporting amb + * Copyright (C) 2022-2024 Bill Hails + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "lambda.h" + +LamExp *lamPerformSimplifications(LamExp *); + +#endif diff --git a/src/main.c b/src/main.c index 88cb877..93b8f63 100644 --- a/src/main.c +++ b/src/main.c @@ -27,6 +27,7 @@ #include "ast_debug.h" #include "lambda_debug.h" #include "lambda_conversion.h" +#include "lambda_simplification.h" #include "annotate.h" #include "anf.h" #include "anf_normalize.h" @@ -232,6 +233,8 @@ static LamExp *convertProg(AstProg *prog) { if (hadErrors()) { exit(1); } + exp = lamPerformSimplifications(exp); + REPLACE_PROTECT(save, exp); if (lambda_flag) { ppLamExp(exp); eprintf("\n");