From 6866c7133d5aa7184f0a77edde5a1ca572348254 Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Mon, 7 Dec 2020 18:52:33 -0700 Subject: [PATCH] Map destructuring --- src/kiss/Helpers.hx | 5 ++++- src/kiss/SpecialForms.hx | 29 +++++++++++++++++++---------- src/test/cases/BasicTestCase.kiss | 7 ++++++- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/kiss/Helpers.hx b/src/kiss/Helpers.hx index d631316..eb5804a 100644 --- a/src/kiss/Helpers.hx +++ b/src/kiss/Helpers.hx @@ -146,7 +146,10 @@ class Helpers { }; } - // TODO use let instead of begin to make the args immutable by default + // To make function args immutable by default, we would use (let...) instead of (begin...) + // to make the body expression. + // But setting default arguments is so common, and arguments are not settable references, + // so function args are not immutable. return { ret: if (name != null) switch (name.def) { case TypedExp(type, _): Helpers.parseComplexType(type, name); diff --git a/src/kiss/SpecialForms.hx b/src/kiss/SpecialForms.hx index 61c0745..df2277b 100644 --- a/src/kiss/SpecialForms.hx +++ b/src/kiss/SpecialForms.hx @@ -91,6 +91,17 @@ class SpecialForms { EBinop(OpAssign, k.convert(args[0]), k.convert(args[1])).withMacroPosOf(wholeExp); }; + function varName(nameExp:ReaderExp) { + return switch (nameExp.def) { + case Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)}): + name; + case KeyValueExp(_, valueNameExp): + varName(valueNameExp); + default: + throw CompileError.fromExp(nameExp, 'expected a symbol, typed symbol, or keyed symbol for variable name in a var binding'); + }; + } + function toVar(nameExp:ReaderExp, valueExp:ReaderExp, k:KissState, ?isFinal:Bool):Var { // This check seems like unnecessary repetition but it's not. It allows is so that individual destructured bindings can specify mutability return if (isFinal == null) { @@ -101,12 +112,7 @@ class SpecialForms { toVar(nameExp, valueExp, k, true); }; } else { - name: switch (nameExp.def) { - case Symbol(name) | TypedExp(_, {pos: _, def: Symbol(name)}): - name; - default: - throw CompileError.fromExp(nameExp, 'expected a symbol or typed symbol for variable name in a var binding'); - }, + name: varName(nameExp), type: switch (nameExp.def) { case TypedExp(type, _): Helpers.parseComplexType(type, nameExp); @@ -136,10 +142,13 @@ class SpecialForms { // Only evaluate the list expression being destructured once: [toVar(uniqueVarSymbol, valueExp, k, true)].concat([ for (nameExp in nameExps) - toVar(nameExp, - CallExp(Symbol("nth").withPosOf(valueExp), - [uniqueVarSymbol, Symbol(Std.string(idx++)).withPosOf(valueExp)]).withPosOf(valueExp), - k, if (isFinal == false) false else null) + toVar(nameExp, switch (nameExp.def) { + case KeyValueExp(keyExp, nameExp): + CallExp(Symbol("dict-get").withPosOf(valueExp), [uniqueVarSymbol, keyExp]).withPosOf(valueExp); + default: + CallExp(Symbol("nth").withPosOf(valueExp), + [uniqueVarSymbol, Symbol(Std.string(idx++)).withPosOf(valueExp)]).withPosOf(valueExp); + }, k, if (isFinal == false) false else null) ]); default: throw CompileError.fromExp(namesExp, "Can only bind variables to a symbol or list of symbols for destructuring"); diff --git a/src/test/cases/BasicTestCase.kiss b/src/test/cases/BasicTestCase.kiss index 76efe7c..3040e76 100644 --- a/src/test/cases/BasicTestCase.kiss +++ b/src/test/cases/BasicTestCase.kiss @@ -348,4 +348,9 @@ (Assert.equals "me" (dict-get myMap "found")) (doFor =>key value myMap (Assert.isTrue (<= 0 (.indexOf ["hey" "found"] key))) - (Assert.isTrue (<= 0 (.indexOf ["you" "me"] value))))) \ No newline at end of file + (Assert.isTrue (<= 0 (.indexOf ["you" "me"] value)))) + + // Map destructuring: + (let [[=>"hey" v1 =>"found" v2] myMap] + (Assert.equals "you" v1) + (Assert.equals "me" v2))) \ No newline at end of file