From 0b8d1a00e1a62f2ba5da56b32610f5d91d2af0bb Mon Sep 17 00:00:00 2001 From: Nat Quayle Nelson Date: Mon, 7 Dec 2020 18:28:12 -0700 Subject: [PATCH] KeyValueExps allow map literals --- src/kiss/Kiss.hx | 16 ++++++++++++++-- src/kiss/Reader.hx | 6 ++++++ src/test/cases/BasicTestCase.hx | 4 ++++ src/test/cases/BasicTestCase.kiss | 9 ++++++++- 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/kiss/Kiss.hx b/src/kiss/Kiss.hx index eb94370..7444049 100644 --- a/src/kiss/Kiss.hx +++ b/src/kiss/Kiss.hx @@ -135,8 +135,18 @@ class Kiss { ECast(convert(innerExp), if (type.length > 0) Helpers.parseComplexType(type, exp) else null).withMacroPosOf(wholeExp); */ case ListExp(elements): - var arrayDecl = EArrayDecl([for (elementExp in elements) convert(elementExp)]).withMacroPosOf(exp); - if (k.wrapListExps) { + var isMap = false; + var arrayDecl = EArrayDecl([ + for (elementExp in elements) { + switch (elementExp.def) { + case KeyValueExp(_, _): + isMap = true; + default: + } + convert(elementExp); + } + ]).withMacroPosOf(exp); + if (!isMap && k.wrapListExps) { ENew({ pack: ["kiss"], name: "List" @@ -148,6 +158,8 @@ class Kiss { Context.parse(code, exp.macroPos()); case FieldExp(field, innerExp): EField(convert(innerExp), field).withMacroPosOf(exp); + case KeyValueExp(keyExp, valueExp): + EBinop(OpArrow, convert(keyExp), convert(valueExp)).withMacroPosOf(exp); default: throw CompileError.fromExp(exp, 'conversion not implemented'); }; diff --git a/src/kiss/Reader.hx b/src/kiss/Reader.hx index c43335a..d5074c6 100644 --- a/src/kiss/Reader.hx +++ b/src/kiss/Reader.hx @@ -19,6 +19,7 @@ enum ReaderExpDef { TypedExp(path:String, exp:ReaderExp); // :type [exp] MetaExp(meta:String, exp:ReaderExp); // &meta [exp] FieldExp(field:String, exp:ReaderExp); // .field [exp] + KeyValueExp(key:ReaderExp, value:ReaderExp); // =>key value } typedef ReadFunction = (Stream) -> Null; @@ -57,6 +58,9 @@ class Reader { // Lets you dot-access a function result without binding it to a name readTable["."] = (stream:Stream) -> FieldExp(nextToken(stream, "a field name"), assertRead(stream, readTable)); + // Lets you construct key-value pairs for map literals or for-loops + readTable["=>"] = (stream:Stream) -> KeyValueExp(assertRead(stream, readTable), assertRead(stream, readTable)); + // Because macro keys are sorted by length and peekChars(0) returns "", this will be used as the default reader macro: readTable[""] = (stream) -> Symbol(nextToken(stream, "a symbol name")); // TODO make - A macro for numerical negation @@ -173,6 +177,8 @@ class Reader { '&$meta ${exp.def.toString()}'; case FieldExp(field, exp): '.$field ${exp.def.toString()}'; + case KeyValueExp(keyExp, valueExp): + '=>${keyExp.def.toString()} ${valueExp.def.toString()}'; } } } diff --git a/src/test/cases/BasicTestCase.hx b/src/test/cases/BasicTestCase.hx index a372b91..5ee5948 100644 --- a/src/test/cases/BasicTestCase.hx +++ b/src/test/cases/BasicTestCase.hx @@ -242,6 +242,10 @@ class BasicTestCase extends Test { function testCase() { _testCase(); } + + function testMaps() { + _testMaps(); + } } class BasicObject { diff --git a/src/test/cases/BasicTestCase.kiss b/src/test/cases/BasicTestCase.kiss index 15d231a..3e815df 100644 --- a/src/test/cases/BasicTestCase.kiss +++ b/src/test/cases/BasicTestCase.kiss @@ -339,4 +339,11 @@ ((Some "hey") (Assert.pass)) (otherwise (Assert.fail))) (Assert.equals 5 (case (toOption 0) - (otherwise 5)))) \ No newline at end of file + (otherwise 5)))) + +(defun _testMaps [] + (deflocal :Map myMap [=>"hey" "you" + =>"found" "me"]) + (Assert.equals "you" (dict-get myMap "hey")) + (Assert.equals "me" (dict-get myMap "found")) + ) \ No newline at end of file