From 08ff68e9ebca2d9b0b9ecf15297595b4caeecce5 Mon Sep 17 00:00:00 2001 From: An Ko Date: Tue, 26 Jan 2016 16:27:11 +0100 Subject: [PATCH 01/27] =?UTF-8?q?Doc=20typo:=20colon=20=E2=86=92=20quote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These property changes used to treat atoms that start with a colon the same way that lists that are a quotation are treated now. --- doc/basics-reference.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/basics-reference.markdown b/doc/basics-reference.markdown index 0f3d6a8..5e19b99 100644 --- a/doc/basics-reference.markdown +++ b/doc/basics-reference.markdown @@ -374,7 +374,7 @@ Property access uses the `.` macro. If you wish you could just write those as `a.b.c` in eslisp code, use the [*eslisp-propertify*][10] user-macro. -For *computed* property access, omit the leading colon. +For *computed* property access, omit the quote. From 8ac07725b71a7223d6740346786157951d143c24 Mon Sep 17 00:00:00 2001 From: An Ko Date: Tue, 26 Jan 2016 16:40:50 +0100 Subject: [PATCH 02/27] Refactor: Remove unused 2nd arg from unwrap-quote Relics from the past shall haunt us no more. --- src/built-in-macros.ls | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 231f5ca..e90de59 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -101,7 +101,7 @@ function-type = (type) -> (params, ...rest) -> is-atom = (node, name) -> node.type is \atom and node.value is name -unwrap-quote = (node, string-is-computed) -> +unwrap-quote = (node) -> | node.type is \list and node.values.0 `is-atom` \quote => computed : false node : node.values.1 @@ -204,7 +204,7 @@ contents = if not name? throw Error "Expected #{type}ter in property #i to have a name" - {node, computed} = unwrap-quote name, true + {node, computed} = unwrap-quote name unless computed or node.type is \atom throw Error "Expected name of #{type}ter in property #i to be a quoted @@ -253,7 +253,7 @@ contents = if not name? throw Error "Expected method in property #i to have a name" - {node, computed} = unwrap-quote name, true + {node, computed} = unwrap-quote name unless computed or node.type is \atom throw Error "Expected name of method in property #i to be a quoted atom @@ -311,7 +311,7 @@ contents = shorthand : true | args.length is 2 => - {node, computed} = unwrap-quote args.0, true + {node, computed} = unwrap-quote args.0 if not computed and node.type isnt \atom throw Error "Expected name of property #i to be an expression or @@ -433,7 +433,7 @@ contents = \. : do join-members = (host, prop) -> - {node, computed} = unwrap-quote prop, false + {node, computed} = unwrap-quote prop if not computed and node.type isnt \atom throw Error "Expected quoted name of property getter to be an atom" From 675fa248a79ea6909a2aef80d7b9238496285801 Mon Sep 17 00:00:00 2001 From: An Ko Date: Tue, 26 Jan 2016 16:43:38 +0100 Subject: [PATCH 03/27] =?UTF-8?q?Refactor:=20rename=20unwrap-quote=20?= =?UTF-8?q?=E2=86=92=20maybe-unwrap-quote?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since it doesn't *always* unwrap a quote; just when it detects one. --- src/built-in-macros.ls | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index e90de59..6a43983 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -101,7 +101,7 @@ function-type = (type) -> (params, ...rest) -> is-atom = (node, name) -> node.type is \atom and node.value is name -unwrap-quote = (node) -> +maybe-unwrap-quote = (node) -> | node.type is \list and node.values.0 `is-atom` \quote => computed : false node : node.values.1 @@ -204,7 +204,7 @@ contents = if not name? throw Error "Expected #{type}ter in property #i to have a name" - {node, computed} = unwrap-quote name + {node, computed} = maybe-unwrap-quote name unless computed or node.type is \atom throw Error "Expected name of #{type}ter in property #i to be a quoted @@ -253,7 +253,7 @@ contents = if not name? throw Error "Expected method in property #i to have a name" - {node, computed} = unwrap-quote name + {node, computed} = maybe-unwrap-quote name unless computed or node.type is \atom throw Error "Expected name of method in property #i to be a quoted atom @@ -311,7 +311,7 @@ contents = shorthand : true | args.length is 2 => - {node, computed} = unwrap-quote args.0 + {node, computed} = maybe-unwrap-quote args.0 if not computed and node.type isnt \atom throw Error "Expected name of property #i to be an expression or @@ -433,7 +433,7 @@ contents = \. : do join-members = (host, prop) -> - {node, computed} = unwrap-quote prop + {node, computed} = maybe-unwrap-quote prop if not computed and node.type isnt \atom throw Error "Expected quoted name of property getter to be an atom" From 911f8463c2146ecbc152564c3f2f57a1338254a0 Mon Sep 17 00:00:00 2001 From: An Ko Date: Tue, 26 Jan 2016 17:36:48 +0100 Subject: [PATCH 04/27] Refactor: Extract is-list check, clarify is-atom Just for easier reading. --- src/built-in-macros.ls | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 6a43983..4d0c82a 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -99,13 +99,19 @@ function-type = (type) -> (params, ...rest) -> params : params body : optionally-implicit-block-statement this, rest -is-atom = (node, name) -> node.type is \atom and node.value is name +is-atom = (node, name) -> + type-ok = node.type is \atom + value-ok = if name then (node.value is name) else true + + type-ok and value-ok + +is-list = (node) -> node.type is \list maybe-unwrap-quote = (node) -> - | node.type is \list and node.values.0 `is-atom` \quote => + if (is-list node) and (is-atom node.values.0, \quote) computed : false node : node.values.1 - | otherwise => + else computed : true node : node From df3476d872006e5e3fc733e6346fc94863543730 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 02:44:06 +0100 Subject: [PATCH 05/27] Refactor: Remove redundant case in `.` macro The `otherwise`-branch covers the same case. --- src/built-in-macros.ls | 1 - 1 file changed, 1 deletion(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 4d0c82a..e8d8f9f 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -455,7 +455,6 @@ contents = switch | &length is 0 => throw Error "dot called with no arguments" | &length is 1 => @compile host - | &length is 2 => join-members.call this, (@compile host), &1 | otherwise => host = @compile host for i from 1 til &length From 0e9ca6a6b9879c1cb2632bea044d42dac0f6fb42 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 03:11:56 +0100 Subject: [PATCH 06/27] Refactor: Simplify `.` macro implementation It's safe to `@compile` things multiple times (it's a no-op when done again), and this `fold1` is neater than a loop. Seems like `coerce-property` will always fail its conditional unless passed the `string-is-computed` argument (in which case it's a no-op), so the call was redundant. --- src/built-in-macros.ls | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index e8d8f9f..6702019 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -1,4 +1,4 @@ -{ map, zip, concat-map } = require \prelude-ls +{ map, zip, concat-map, fold1 } = require \prelude-ls { is-expression } = require \esutils .ast statementify = require \./es-statementify { @@ -438,28 +438,29 @@ contents = argument : @compile arg \. : do - join-members = (host, prop) -> - {node, computed} = maybe-unwrap-quote prop - if not computed and node.type isnt \atom + join-as-member-expression = (host-node, prop-node) -> + + host = @compile host-node + + { node : prop-node, computed } = maybe-unwrap-quote prop-node + + if not computed and prop-node.type isnt \atom throw Error "Expected quoted name of property getter to be an atom" - {node : prop, computed} = coerce-property (@compile node), computed, false + prop = @compile prop-node type : \MemberExpression computed : computed object : host property : prop - (host) -> - switch + -> | &length is 0 => throw Error "dot called with no arguments" - | &length is 1 => @compile host + | &length is 1 => @compile &0 | otherwise => - host = @compile host - for i from 1 til &length - host = join-members.call this, host, &[i] - host + [].slice.call arguments + |> fold1 join-as-member-expression.bind @ \lambda : function-type \FunctionExpression From 307490b7089daaf8483639bcd654f475fb7727f0 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 03:16:03 +0100 Subject: [PATCH 07/27] Refactor: Remove redundant stringIsComputed arg --- src/built-in-macros.ls | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 6702019..700e3da 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -116,12 +116,10 @@ maybe-unwrap-quote = (node) -> node : node # For some final coercion after compilation, when building the ESTree AST. -coerce-property = (node, computed, string-is-computed) -> - # This should be explicitly overridden and unconditional. Helps with minifiers - # and other things. - | string-is-computed and - node.type is \Literal and - typeof node.value isnt \object => +coerce-property = (node, computed) -> + # This should be explicitly overridden and unconditional. Helps with + # minifiers and other things. + | (node.type is \Literal) and (typeof node.value isnt \object) => node : type : \Literal value : node.value + '' @@ -216,7 +214,7 @@ contents = throw Error "Expected name of #{type}ter in property #i to be a quoted atom or an expression" - {node : name, computed} = coerce-property (@compile node), computed, true + {node : name, computed} = coerce-property (@compile node), computed kind = infer-name "#{type}ter", name, computed unless params?.type is \list @@ -265,7 +263,7 @@ contents = throw Error "Expected name of method in property #i to be a quoted atom or an expression" - {node : name, computed} = coerce-property (@compile node), computed, true + {node : name, computed} = coerce-property (@compile node), computed method = infer-name 'method', name, computed if not params? or params.type isnt \list @@ -323,7 +321,7 @@ contents = throw Error "Expected name of property #i to be an expression or quoted atom" - {node : key, computed} = coerce-property (@compile node), computed, true + {node : key, computed} = coerce-property (@compile node), computed type : \Property kind : \init From 45fb7c9a80c00edc1ff3e180df2c8a819910d086 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 03:19:35 +0100 Subject: [PATCH 08/27] Refactor: Simplify `coerce-property` logic --- src/built-in-macros.ls | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 700e3da..47efdd7 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -119,14 +119,10 @@ maybe-unwrap-quote = (node) -> coerce-property = (node, computed) -> # This should be explicitly overridden and unconditional. Helps with # minifiers and other things. - | (node.type is \Literal) and (typeof node.value isnt \object) => - node : - type : \Literal - value : node.value + '' - computed : false - | otherwise => + | node.type is \Literal => node : node - computed : computed + computed : false + | otherwise => { node, computed } contents = \+ : n-ary-expr \+ From c940043b29cce60c4fcc84852a3a9c67ec1bf29b Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 03:29:25 +0100 Subject: [PATCH 09/27] Refactor: Localise maybeUnwrapQuote error handling That error checking was duplicated a lot. This error message should be specific enough. --- src/built-in-macros.ls | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 47efdd7..880ccf8 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -109,11 +109,19 @@ is-list = (node) -> node.type is \list maybe-unwrap-quote = (node) -> if (is-list node) and (is-atom node.values.0, \quote) + + quoted-thing = node.values.1 + + unless is-atom quoted-thing + throw Error "Unexpected quoted property #{quoted-thing.type}: \ + expected atom" + computed : false - node : node.values.1 + node : quoted-thing + else computed : true - node : node + node : node # For some final coercion after compilation, when building the ESTree AST. coerce-property = (node, computed) -> @@ -206,10 +214,6 @@ contents = {node, computed} = maybe-unwrap-quote name - unless computed or node.type is \atom - throw Error "Expected name of #{type}ter in property #i to be a quoted - atom or an expression" - {node : name, computed} = coerce-property (@compile node), computed kind = infer-name "#{type}ter", name, computed @@ -255,10 +259,6 @@ contents = {node, computed} = maybe-unwrap-quote name - unless computed or node.type is \atom - throw Error "Expected name of method in property #i to be a quoted atom - or an expression" - {node : name, computed} = coerce-property (@compile node), computed method = infer-name 'method', name, computed @@ -313,10 +313,6 @@ contents = | args.length is 2 => {node, computed} = maybe-unwrap-quote args.0 - if not computed and node.type isnt \atom - throw Error "Expected name of property #i to be an expression or - quoted atom" - {node : key, computed} = coerce-property (@compile node), computed type : \Property @@ -439,9 +435,6 @@ contents = { node : prop-node, computed } = maybe-unwrap-quote prop-node - if not computed and prop-node.type isnt \atom - throw Error "Expected quoted name of property getter to be an atom" - prop = @compile prop-node type : \MemberExpression From 4c2c454e460946ffae52260a2ec7a53b4a2e197c Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 14:12:08 +0100 Subject: [PATCH 10/27] Refactor: Inline "coerce property" checks It was pretty confusing to me what coerceProperty was doing. Because it's been reduced to just a single conditional at this point, might as well write it as a conditional instead wherever it's used: that requires holding less state in mind when reading. --- src/built-in-macros.ls | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 880ccf8..68d538a 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -123,15 +123,6 @@ maybe-unwrap-quote = (node) -> computed : true node : node -# For some final coercion after compilation, when building the ESTree AST. -coerce-property = (node, computed) -> - # This should be explicitly overridden and unconditional. Helps with - # minifiers and other things. - | node.type is \Literal => - node : node - computed : false - | otherwise => { node, computed } - contents = \+ : n-ary-expr \+ \- : n-ary-expr \- @@ -214,7 +205,9 @@ contents = {node, computed} = maybe-unwrap-quote name - {node : name, computed} = coerce-property (@compile node), computed + name = @compile node + if name.type is \Literal + computed := false kind = infer-name "#{type}ter", name, computed unless params?.type is \list @@ -259,7 +252,9 @@ contents = {node, computed} = maybe-unwrap-quote name - {node : name, computed} = coerce-property (@compile node), computed + name := @compile node + if name.type is \Literal + computed := false method = infer-name 'method', name, computed if not params? or params.type isnt \list @@ -313,7 +308,9 @@ contents = | args.length is 2 => {node, computed} = maybe-unwrap-quote args.0 - {node : key, computed} = coerce-property (@compile node), computed + key = @compile node + if key.type is \Literal + computed := false type : \Property kind : \init From 63723a5fcb4ae8ce5cf8ca6a2b6f7ba9f3074682 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 14:24:42 +0100 Subject: [PATCH 11/27] Refactor: Inline checkList function There were lots of lines of code between where this was defined and the *one place* where it was used, so I inlined it for readability. --- src/built-in-macros.ls | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 68d538a..754ed50 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -187,9 +187,6 @@ contents = elements : elements.map @compile \object : do - check-list = (list, i) -> - | list? and list.type is \list => list.values - | otherwise => throw Error "Expected property #i to be a list" infer-name = (prefix, name, computed) -> if computed @@ -330,10 +327,16 @@ contents = | otherwise => compile-method.call this, i, args - -> + (...args) -> type : \ObjectExpression - properties : for args, i in arguments - compile-list.call this, i, (check-list args, i) + properties : args.map (arg, i) ~> + + if is-list arg + compile-list.call @, i, arg.values + else + throw Error "Unexpected argument to object macro: \ + expected properties to be lists, but \ + argument #i type was #{arg.type}" \var : (name, value) -> if &length > 2 From bd3e9210aad0abdc61fe7231a645df77ece430d0 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 14:46:55 +0100 Subject: [PATCH 12/27] Refactor: Catch+rethrow instead of passing arg i That `i` parameter standing for the argument index currently being processed is non-descriptive and clutters up the place. It's neater to prepend the "there was a problem with argument #i" type of error text in a central place, and have the leaf conditionals just throw an error with the details. Also, few of these error cases were being tested. --- src/built-in-macros.ls | 59 +++++++++++++++++++++++------------------- test.ls | 28 ++++++++++++++++++++ 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 754ed50..14a6a59 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -196,9 +196,16 @@ contents = else "#prefix #{name.name}" - compile-get-set = (i, type, [name, params, ...body]) -> + compile-get-set = (type, args) -> + # type is either "get" or "set" + + if not args + throw Error "No arguments!" + + [name, params, ...body] = args + if not name? - throw Error "Expected #{type}ter in property #i to have a name" + throw Error "No #{type}ter name" {node, computed} = maybe-unwrap-quote name @@ -208,7 +215,7 @@ contents = kind = infer-name "#{type}ter", name, computed unless params?.type is \list - throw Error "Expected #{kind} in property #i to have a parameter list" + throw Error "Expected #{kind} to have a parameter list" params .= values @@ -217,14 +224,14 @@ contents = # stringifier itself. if type is \get if params.length isnt 0 - throw Error "Expected #{kind} in property #i to have no parameters" + throw Error "Expected #{kind} to have no parameters" else # type is \set if params.length isnt 1 - throw Error "Expected #{kind} in property #i to have exactly one \ + throw Error "Expected #{kind} to have exactly one \ parameter" param = params.0 if param.type isnt \atom - throw Error "Expected parameter for #{kind} in property #i to be an \ + throw Error "Expected parameter for #{kind} to be an \ identifier" params = [ type : \Identifier @@ -243,9 +250,9 @@ contents = body : optionally-implicit-block-statement this, body expression : false - compile-method = (i, [name, params, ...body]) -> + compile-method = ([name, params, ...body]) -> if not name? - throw Error "Expected method in property #i to have a name" + throw Error "Expected method to have a name" {node, computed} = maybe-unwrap-quote name @@ -255,12 +262,12 @@ contents = method = infer-name 'method', name, computed if not params? or params.type isnt \list - throw Error "Expected #method in property #i to have a parameter \ + throw Error "Expected #method to have a parameter \ list" params = for param, j in params.values if param.type isnt \atom - throw Error "Expected parameter #j for #method in property #i to be \ + throw Error "Expected parameter #j for #method to be \ an identifier" type : \Identifier name : param.value @@ -277,20 +284,17 @@ contents = body : optionally-implicit-block-statement this, body expression : false - compile-list = (i, args) -> + compile-property-list = (args) -> | args.length is 0 => - throw Error "Expected at least two arguments in property #i" + throw Error "Got empty list (expected list to have contents)" | args.length is 1 => node = args.0 - if node.type isnt \list - throw Error "Expected name in property #i to be a quoted atom" - [type, node] = node.values - unless type `is-atom` \quote and node.type is \atom - throw Error "Expected name in property #i to be a quoted atom" + unless (is-atom type, \quote) and is-atom node + throw Error "Invalid single-element list (expected a pattern of (quote ))" type : \Property kind : \init @@ -318,25 +322,28 @@ contents = # Check this before compilation and macro resolution to ensure that # neither can affect this, but that it can be avoided in the edge case if # needed with `(id get)` or `(id set)`, where `(macro id (lambda (x) x))`. - | args.0 `is-atom` \get or args.0 `is-atom` \set => - compile-get-set.call this, i, args.0.value, args[1 til] + | is-atom args.0 and (args.0.value in <[ get set ]>) => + compile-get-set.call this, args.0.value, args[1 til] # Reserve this for future generator use. - | args.0.type `is-atom` \* => - throw Error "Unexpected generator method in property #i" + | args.0 `is-atom` \* => + throw Error "Unexpected '*' (generator methods not yet implemented)" - | otherwise => compile-method.call this, i, args + | otherwise => compile-method.call this, args (...args) -> type : \ObjectExpression properties : args.map (arg, i) ~> if is-list arg - compile-list.call @, i, arg.values + try + compile-property-list.call @, arg.values + catch e + e.message = "Unexpected object macro argument #i: " + e.message + throw e else - throw Error "Unexpected argument to object macro: \ - expected properties to be lists, but \ - argument #i type was #{arg.type}" + throw Error "Unexpected object macro argument #i: \ + Got #{arg.type} (expected list)" \var : (name, value) -> if &length > 2 diff --git a/test.ls b/test.ls index f1dfa06..186a8db 100755 --- a/test.ls +++ b/test.ls @@ -717,6 +717,10 @@ test "array macro can be empty" -> esl "(array)" ..`@equals` "[];" +test "object macro can be empty" -> + esl "(object)" + ..`@equals` "({});" + test "object macro produces object expression" -> esl "(object ('a 1) ('b 2))" ..`@equals` "({\n a: 1,\n b: 2\n});" @@ -828,6 +832,30 @@ test "object macro compiles complex ES6 object" -> }); ''' +test "object macro empty list argument raises error" -> + try + esl '(object ())' + catch e + e.message `@equals` "Unexpected object macro argument 0: Got empty list (expected list to have contents)" + return + @fail "No error thrown" + +test "object macro non-list argument raises error" -> + try + esl '(object "hi")' + catch e + e.message `@equals` "Unexpected object macro argument 0: Got string (expected list)" + return + @fail "No error thrown" + +test "object macro not-a-quoted-atom in argument raises error" -> + try + esl '(object ((a x)))' + catch e + e.message `@equals` "Unexpected object macro argument 0: Invalid single-element list (expected a pattern of (quote ))" + return + @fail "No error thrown" + test "macro producing an object literal" -> esl "(macro obj (lambda () (return '(object ('a 1))))) (obj)" From c01cd6a95c2ec7ec3219a37b55c75bca7c93b44f Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 15:55:23 +0100 Subject: [PATCH 13/27] Distinguish pattern errors from generic ones In the previous commit, generic JavaScript errors (like TypeError, etc) would wrongly have the "Unexpected object macro argument #i: " text prepended to their message. That goes away by throwing a specific error type. --- src/built-in-macros.ls | 43 ++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 14a6a59..6be93a4 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -187,6 +187,21 @@ contents = elements : elements.map @compile \object : do + # This macro needs to detect patterns in its arguments, e.g. + # + # (object (get 'a () (return 1))) + # + # where (get ) is a pattern. It is split + # into methods that handle different kinds of patterns. + + # Specific Error type, to be thrown just in this macro when the user has + # provided an invalid argument pattern. + # + # This makes error handling neater: the top-level macro function catches + # this type of Errors and prepends information about which parameter was + # being processed when it occurred. + class ObjectParamError extends Error + (@message) ~> infer-name = (prefix, name, computed) -> if computed @@ -205,7 +220,7 @@ contents = [name, params, ...body] = args if not name? - throw Error "No #{type}ter name" + throw ObjectParamError "No #{type}ter name" {node, computed} = maybe-unwrap-quote name @@ -215,7 +230,7 @@ contents = kind = infer-name "#{type}ter", name, computed unless params?.type is \list - throw Error "Expected #{kind} to have a parameter list" + throw ObjectParamError "Expected #{kind} to have a parameter list" params .= values @@ -224,14 +239,14 @@ contents = # stringifier itself. if type is \get if params.length isnt 0 - throw Error "Expected #{kind} to have no parameters" + throw ObjectParamError "Expected #{kind} to have no parameters" else # type is \set if params.length isnt 1 - throw Error "Expected #{kind} to have exactly one \ + throw ObjectParamError "Expected #{kind} to have exactly one \ parameter" param = params.0 if param.type isnt \atom - throw Error "Expected parameter for #{kind} to be an \ + throw ObjectParamError "Expected parameter for #{kind} to be an \ identifier" params = [ type : \Identifier @@ -252,7 +267,7 @@ contents = compile-method = ([name, params, ...body]) -> if not name? - throw Error "Expected method to have a name" + throw ObjectParamError "Expected method to have a name" {node, computed} = maybe-unwrap-quote name @@ -262,12 +277,12 @@ contents = method = infer-name 'method', name, computed if not params? or params.type isnt \list - throw Error "Expected #method to have a parameter \ + throw ObjectParamError "Expected #method to have a parameter \ list" params = for param, j in params.values if param.type isnt \atom - throw Error "Expected parameter #j for #method to be \ + throw ObjectParamError "Expected parameter #j for #method to be \ an identifier" type : \Identifier name : param.value @@ -286,7 +301,7 @@ contents = compile-property-list = (args) -> | args.length is 0 => - throw Error "Got empty list (expected list to have contents)" + throw ObjectParamError "Got empty list (expected list to have contents)" | args.length is 1 => node = args.0 @@ -294,7 +309,7 @@ contents = [type, node] = node.values unless (is-atom type, \quote) and is-atom node - throw Error "Invalid single-element list (expected a pattern of (quote ))" + throw ObjectParamError "Invalid single-element list (expected a pattern of (quote ))" type : \Property kind : \init @@ -327,7 +342,7 @@ contents = # Reserve this for future generator use. | args.0 `is-atom` \* => - throw Error "Unexpected '*' (generator methods not yet implemented)" + throw ObjectParamError "Unexpected '*' (generator methods not yet implemented)" | otherwise => compile-method.call this, args @@ -339,8 +354,12 @@ contents = try compile-property-list.call @, arg.values catch e - e.message = "Unexpected object macro argument #i: " + e.message + # To object parameter errors, prepend the argument index. + if e instanceof ObjectParamError + e.message = "Unexpected object macro argument #i: " + e.message + throw e + else throw Error "Unexpected object macro argument #i: \ Got #{arg.type} (expected list)" From 2d1f21e2aaa0f21667ba49d278ed3d38e9245276 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 15:58:23 +0100 Subject: [PATCH 14/27] Add TODO for implement generator method property --- src/built-in-macros.ls | 1 + 1 file changed, 1 insertion(+) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 6be93a4..3b7c097 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -341,6 +341,7 @@ contents = compile-get-set.call this, args.0.value, args[1 til] # Reserve this for future generator use. + # TODO Implement | args.0 `is-atom` \* => throw ObjectParamError "Unexpected '*' (generator methods not yet implemented)" From 9ca20fda6c04892efd600981bdf95b6f1df87642 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 17:19:58 +0100 Subject: [PATCH 15/27] Refactor naming of parameters in errors --- src/built-in-macros.ls | 58 +++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 3b7c097..7506372 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -203,58 +203,49 @@ contents = class ObjectParamError extends Error (@message) ~> - infer-name = (prefix, name, computed) -> - if computed - prefix - else if typeof name.type is \Literal - "#prefix #{name.value}" - else - "#prefix #{name.name}" + compile-get-set = (kind, [name, params, ...body]) -> + # kind is either "get" or "set" - compile-get-set = (type, args) -> - # type is either "get" or "set" - - if not args - throw Error "No arguments!" - - [name, params, ...body] = args + # What the thing being compiled is called in human-readable errors + readable-kind-name = kind + "ter" if not name? - throw ObjectParamError "No #{type}ter name" + throw ObjectParamError "No #readable-kind-name name" {node, computed} = maybe-unwrap-quote name name = @compile node - if name.type is \Literal + if name.kind is \Literal computed := false - kind = infer-name "#{type}ter", name, computed - unless params?.type is \list - throw ObjectParamError "Expected #{kind} to have a parameter list" + unless is-list params + throw ObjectParamError "Expected #readable-kind-name to have a \ + parameter list" params .= values # Catch this error here, to return a more sensible, helpful error message # than merely an InvalidAstError referencing property names from the # stringifier itself. - if type is \get + if kind is \get if params.length isnt 0 - throw ObjectParamError "Expected #{kind} to have no parameters" - else # type is \set + throw ObjectParamError "Expected #readable-kind-name to have \ + no parameters" + else # kind is \set if params.length isnt 1 - throw ObjectParamError "Expected #{kind} to have exactly one \ - parameter" + throw ObjectParamError "Expected #readable-kind-name to have \ + exactly one parameter" param = params.0 - if param.type isnt \atom - throw ObjectParamError "Expected parameter for #{kind} to be an \ - identifier" + if not is-atom param + throw ObjectParamError "Expected parameter for \ + #readable-kind-name to be an identifier" params = [ type : \Identifier name : param.value ] type : \Property - kind : type + kind : kind key : name # The initial check doesn't cover the compiled case. computed : computed @@ -274,16 +265,19 @@ contents = name := @compile node if name.type is \Literal computed := false - method = infer-name 'method', name, computed + + readable-kind-name = 'method'+ switch name.type is \Identifier + | true => " '#{name.name}'" + | false => "" if not params? or params.type isnt \list - throw ObjectParamError "Expected #method to have a parameter \ + throw ObjectParamError "Expected #readable-kind-name to have a parameter \ list" params = for param, j in params.values if param.type isnt \atom - throw ObjectParamError "Expected parameter #j for #method to be \ - an identifier" + throw ObjectParamError "Expected parameter #j for #readable-kind-name + to be an identifier" type : \Identifier name : param.value From c57fa2bf17b5a8cd7a3bffb7db42b2641a03d32b Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 17:47:12 +0100 Subject: [PATCH 16/27] Use function macro to compile get/set Reduces duplication and guards against future changes to how the function macro is implemented. --- src/built-in-macros.ls | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 7506372..37316c7 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -203,7 +203,7 @@ contents = class ObjectParamError extends Error (@message) ~> - compile-get-set = (kind, [name, params, ...body]) -> + compile-get-set = (kind, [name, ...function-macro-arguments-part]) -> # kind is either "get" or "set" # What the thing being compiled is called in human-readable errors @@ -218,6 +218,10 @@ contents = if name.kind is \Literal computed := false + # We'll only check the parameters here; the function expression macro can + # check the body. + [ params, _ ] = function-macro-arguments-part + unless is-list params throw ObjectParamError "Expected #readable-kind-name to have a \ parameter list" @@ -249,12 +253,10 @@ contents = key : name # The initial check doesn't cover the compiled case. computed : computed - value : - type : \FunctionExpression - id : null - params : params - body : optionally-implicit-block-statement this, body - expression : false + value : do + (function-type \FunctionExpression) + .apply this, function-macro-arguments-part + ..expression = false compile-method = ([name, params, ...body]) -> if not name? From c81e1caaf4fa29e987eb04eb6ff770d55b14c742 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 17:48:06 +0100 Subject: [PATCH 17/27] Clarify get/set parameters error and test it --- src/built-in-macros.ls | 5 +++-- test.ls | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 37316c7..12f9d01 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -223,8 +223,9 @@ contents = [ params, _ ] = function-macro-arguments-part unless is-list params - throw ObjectParamError "Expected #readable-kind-name to have a \ - parameter list" + throw ObjectParamError "Unexpected #readable-kind-name part \ + (got #{params.type}; \ + expected list of parameters)" params .= values diff --git a/test.ls b/test.ls index 186a8db..d2bd4ec 100755 --- a/test.ls +++ b/test.ls @@ -856,6 +856,15 @@ test "object macro not-a-quoted-atom in argument raises error" -> return @fail "No error thrown" +test "object macro setter with bad argument list raises error" -> + try + esl '(object (set x x))' + catch e + e.message `@equals` 'Unexpected object macro argument 0: Unexpected setter part (got atom; expected list of parameters)' + + return + @fail "No error thrown" + test "macro producing an object literal" -> esl "(macro obj (lambda () (return '(object ('a 1))))) (obj)" From b4f51cbe6f16ed9ec4098a67b7bd243703f3efb4 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 17:54:14 +0100 Subject: [PATCH 18/27] Test that empty setter body works --- test.ls | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test.ls b/test.ls index d2bd4ec..addd0a9 100755 --- a/test.ls +++ b/test.ls @@ -749,6 +749,10 @@ test "object macro can create setters" -> esl '(object (set \'a (x) (return 1)))' ..`@equals` '({\n set a(x) {\n return 1;\n }\n});' +test "object macro can create setters with no body" -> + esl '(object (set y (x)))' + ..`@equals` '({\n set [y](x) {\n }\n});' + test "object macro can create computed getters and setters" -> esl '(object (get a ()) (set a (x)))' ..`@equals` ''' From a36923cb9b829401e63298a6cb62a7d36d6f294f Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 17:57:38 +0100 Subject: [PATCH 19/27] Test for get/set parameter count errors --- src/built-in-macros.ls | 4 ++-- test.ls | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 12f9d01..e70b645 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -235,11 +235,11 @@ contents = if kind is \get if params.length isnt 0 throw ObjectParamError "Expected #readable-kind-name to have \ - no parameters" + no parameters (got #{params.length})" else # kind is \set if params.length isnt 1 throw ObjectParamError "Expected #readable-kind-name to have \ - exactly one parameter" + exactly one parameter (got #{params.length})" param = params.0 if not is-atom param throw ObjectParamError "Expected parameter for \ diff --git a/test.ls b/test.ls index addd0a9..cf2f0c3 100755 --- a/test.ls +++ b/test.ls @@ -869,6 +869,24 @@ test "object macro setter with bad argument list raises error" -> return @fail "No error thrown" +test "object macro setter with no arguments raises error" -> + try + esl '(object (set x ()))' + catch e + e.message `@equals` 'Unexpected object macro argument 0: Expected setter to have exactly one parameter (got 0)' + + return + @fail "No error thrown" + +test "object macro getter with arguments raises error" -> + try + esl '(object (get x (y)))' + catch e + e.message `@equals` 'Unexpected object macro argument 0: Expected getter to have no parameters (got 1)' + + return + @fail "No error thrown" + test "macro producing an object literal" -> esl "(macro obj (lambda () (return '(object ('a 1))))) (obj)" From 6190f17fc8bf22eb4dd5dbfa1e20791022afc185 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 17:59:42 +0100 Subject: [PATCH 20/27] Remove get/set parameter type check That's already done by the function macro. --- src/built-in-macros.ls | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index e70b645..ecc9b65 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -240,14 +240,6 @@ contents = if params.length isnt 1 throw ObjectParamError "Expected #readable-kind-name to have \ exactly one parameter (got #{params.length})" - param = params.0 - if not is-atom param - throw ObjectParamError "Expected parameter for \ - #readable-kind-name to be an identifier" - params = [ - type : \Identifier - name : param.value - ] type : \Property kind : kind From 081b57e6d72e6d078d687cf9b6e0d264dea36b3c Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 18:04:43 +0100 Subject: [PATCH 21/27] Simplify get/set error checking conditional --- src/built-in-macros.ls | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index ecc9b65..7a2d25c 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -232,12 +232,11 @@ contents = # Catch this error here, to return a more sensible, helpful error message # than merely an InvalidAstError referencing property names from the # stringifier itself. - if kind is \get - if params.length isnt 0 + switch + | kind is \get and params.length isnt 0 throw ObjectParamError "Expected #readable-kind-name to have \ no parameters (got #{params.length})" - else # kind is \set - if params.length isnt 1 + | kind is \set and params.length isnt 1 throw ObjectParamError "Expected #readable-kind-name to have \ exactly one parameter (got #{params.length})" From d4f9acd1ec63ff4fa80e8de5ba775d65e752ef56 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 18:08:11 +0100 Subject: [PATCH 22/27] Use function macro to compile methods Similarly to set/get, this keeps all the function-constructing stuff in one place, including error checking. --- src/built-in-macros.ls | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 7a2d25c..aaa4adb 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -250,9 +250,11 @@ contents = .apply this, function-macro-arguments-part ..expression = false - compile-method = ([name, params, ...body]) -> - if not name? - throw ObjectParamError "Expected method to have a name" + compile-method = ([name, ...function-macro-arguments-part]) -> + + if not name? then throw ObjectParamError "Expected method to have a name" + + [ params, _ ] = function-macro-arguments-part {node, computed} = maybe-unwrap-quote name @@ -264,28 +266,19 @@ contents = | true => " '#{name.name}'" | false => "" - if not params? or params.type isnt \list - throw ObjectParamError "Expected #readable-kind-name to have a parameter \ - list" - - params = for param, j in params.values - if param.type isnt \atom - throw ObjectParamError "Expected parameter #j for #readable-kind-name - to be an identifier" - type : \Identifier - name : param.value + if not params? or not is-list params + throw ObjectParamError "Expected #readable-kind-name to have a \ + parameter list" type : \Property kind : \init method : true computed : computed key : name - value : - type : \FunctionExpression - id : null - params : params - body : optionally-implicit-block-statement this, body - expression : false + value : do + (function-type \FunctionExpression) + .apply this, function-macro-arguments-part + ..expression = false compile-property-list = (args) -> | args.length is 0 => From cea6415ba436eba9d224df4a468e194c2af2ac20 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 18:09:55 +0100 Subject: [PATCH 23/27] Don't set `expression` prop on get/set or methods It's unnecessary: the [estree spec for ES5][1] doesn't specify it, and [the estree spec for ES6][2] only specifies it for nodes with type "ArrowFunctionExpression", which we aren't using and which can't be used as getters and setters anyway. [1]: https://github.com/estree/estree/blob/master/spec.md [2]: https://github.com/estree/estree/blob/master/es6.md --- src/built-in-macros.ls | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index aaa4adb..101b595 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -248,7 +248,6 @@ contents = value : do (function-type \FunctionExpression) .apply this, function-macro-arguments-part - ..expression = false compile-method = ([name, ...function-macro-arguments-part]) -> @@ -278,7 +277,6 @@ contents = value : do (function-type \FunctionExpression) .apply this, function-macro-arguments-part - ..expression = false compile-property-list = (args) -> | args.length is 0 => From 4320a457919ddab5a8e867fcc09648766ad01780 Mon Sep 17 00:00:00 2001 From: An Ko Date: Wed, 27 Jan 2016 18:35:00 +0100 Subject: [PATCH 24/27] Clarify what esvalid limit we're working around This does mean we're blocked on https://github.com/estools/esvalid/issues/7 if we want safety. --- src/translate.ls | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/translate.ls b/src/translate.ls index 54116ad..2293942 100644 --- a/src/translate.ls +++ b/src/translate.ls @@ -31,8 +31,10 @@ module.exports = (root-env, ast, options={}) -> |> (.map statementify) err = errors program-ast |> reject ({node}) -> - # These are valid ES6 nodes, and their errors need to be ignored. See - # https://github.com/estools/esvalid/issues/7. + # TODO Because esvalid doesn't yet support ES6 + # https://github.com/estools/esvalid/issues/7 we have to manually ignore + # errors to do with properties that have a computed key. This may miss + # some though! | node.type is \Property => node.computed and node.key?.type not in <[Identifier Literal]> | otherwise => false From 98ddff3c0f7913724c785b6b88fb0c90a8fe166b Mon Sep 17 00:00:00 2001 From: An Ko Date: Thu, 28 Jan 2016 23:28:16 +0100 Subject: [PATCH 25/27] Use "method" atom to denote method properties Consider an ES6 object literal with a method that has empty arguments and an empty body: { d() { } } Before this commit, that would intuitively have been written as (object ('d ())) which failed because the ('d ()) list has 2 elements, which the logic attempted to compile to an ordinary property instead of a method. It's ambiguous with { d: null } if () were to compile to null. It currently compiles to nothing (#32), but the argument stands. This change hence introduces this syntax (object (method 'd ())) similarly to how getters and setters already allow empty bodies with (object (get 'd ()) (set 'd ())) I also moved the check for the initial "get" / "set" / "method" atom above the args.length==2 check, to ensure it takes precedence. --- doc/basics-reference.markdown | 4 ++-- src/built-in-macros.ls | 30 ++++++++++++++++-------------- test.ls | 19 +++++++++++++------ 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/doc/basics-reference.markdown b/doc/basics-reference.markdown index 5e19b99..e984937 100644 --- a/doc/basics-reference.markdown +++ b/doc/basics-reference.markdown @@ -339,7 +339,7 @@ implemented, though, so generator methods are not available. (object ('prop) ((. Symbol 'toStringTag) "foo") - ('method (arg) (return (+ arg 1))) + (method 'methodName (arg) (return (+ arg 1))) (get data () (return 1))) @@ -349,7 +349,7 @@ implemented, though, so generator methods are not available. ({ prop, [Symbol.toStringTag]: 'foo', - method(arg) { + methodName(arg) { return arg + 1; }, get [data]() { diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 101b595..9841d52 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -300,7 +300,22 @@ contents = name : node.value shorthand : true - | args.length is 2 => + | is-atom args.0 and (args.0.value is \method) => + compile-method.call this, args[1 til] + + | is-atom args.0 and (args.0.value in <[ get set ]>) => + compile-get-set.call this, args.0.value, args[1 til] + + | args.0 `is-atom` \* => + # TODO Implement + throw ObjectParamError "Unexpected '*' (generator methods not yet implemented)" + + | otherwise # Assume a key-value pair for a normal object Property + if args.length isnt 2 + throw Error "Not getter, setter, method, or shorthand property, \ + but length is #{args.length} \ + (expected 2: key and value)" + {node, computed} = maybe-unwrap-quote args.0 key = @compile node @@ -313,19 +328,6 @@ contents = key : key value : @compile args.1 - # Check this before compilation and macro resolution to ensure that - # neither can affect this, but that it can be avoided in the edge case if - # needed with `(id get)` or `(id set)`, where `(macro id (lambda (x) x))`. - | is-atom args.0 and (args.0.value in <[ get set ]>) => - compile-get-set.call this, args.0.value, args[1 til] - - # Reserve this for future generator use. - # TODO Implement - | args.0 `is-atom` \* => - throw ObjectParamError "Unexpected '*' (generator methods not yet implemented)" - - | otherwise => compile-method.call this, args - (...args) -> type : \ObjectExpression properties : args.map (arg, i) ~> diff --git a/test.ls b/test.ls index cf2f0c3..ffd8494 100755 --- a/test.ls +++ b/test.ls @@ -745,11 +745,15 @@ test "object macro can create getters" -> esl '(object (get \'a () (return 1)))' ..`@equals` '({\n get a() {\n return 1;\n }\n});' +test "object macro can create getters with empty body" -> + esl '(object (get \'a ()))' + ..`@equals` '({\n get a() {\n }\n});' + test "object macro can create setters" -> esl '(object (set \'a (x) (return 1)))' ..`@equals` '({\n set a(x) {\n return 1;\n }\n});' -test "object macro can create setters with no body" -> +test "object macro can create setters with empty body" -> esl '(object (set y (x)))' ..`@equals` '({\n set [y](x) {\n }\n});' @@ -767,9 +771,10 @@ test "object macro can create computed getters and setters" -> test "object macro's parts can be ES6 methods" -> esl ''' (object - ('a () (return 1)) - ('b (x) (return (+ x 1))) - (c (x y) (return (+ x y 1)))) + (method 'a () (return 1)) + (method 'b (x) (return (+ x 1))) + (method c (x y) (return (+ x y 1))) + (method 'd ())) ; no args, empty method body ''' ..`@equals` """ ({ @@ -781,6 +786,8 @@ test "object macro's parts can be ES6 methods" -> }, [c](x, y) { return x + (y + 1); + }, + d() { } }); """ @@ -804,10 +811,10 @@ test "object macro compiles complex ES6 object" -> (set (. syms 'Sym) (value) ((. wm 'set) this value)) - ('printFoo () + (method 'printFoo () ((. console 'log) (. this 'foo))) - ('concatFoo (value) + (method 'concatFoo (value) (return (+ (. this 'foo) value)))) ''' ..`@equals` ''' From faa44c48beb85a4030224ac4e72f9f7b9e4757d9 Mon Sep 17 00:00:00 2001 From: An Ko Date: Fri, 29 Jan 2016 00:32:33 +0100 Subject: [PATCH 26/27] Make test-all a .PHONY make-target Just in case someone creates a file called "test-all". --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index 73e27ca..94aad49 100644 --- a/makefile +++ b/makefile @@ -26,4 +26,4 @@ test-docs: all doc/how-macros-work.markdown doc/basics-reference.markdown test-all: test test-readme test-docs -.PHONY: all clean test test-readme test-docs +.PHONY: all clean test test-readme test-docs test-all From 6a82f4d17fa2dc85a7f04f2ec57af2c15a924196 Mon Sep 17 00:00:00 2001 From: An Ko Date: Fri, 29 Jan 2016 00:42:27 +0100 Subject: [PATCH 27/27] Throw descriptive errors for method properties --- src/built-in-macros.ls | 31 ++++++++++++++++++------------- test.ls | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/built-in-macros.ls b/src/built-in-macros.ls index 9841d52..850ff1c 100644 --- a/src/built-in-macros.ls +++ b/src/built-in-macros.ls @@ -249,9 +249,14 @@ contents = (function-type \FunctionExpression) .apply this, function-macro-arguments-part - compile-method = ([name, ...function-macro-arguments-part]) -> + compile-method = (args) -> - if not name? then throw ObjectParamError "Expected method to have a name" + if args.length is 0 + throw ObjectParamError "Method has no name or argument list" + + [name, ...function-macro-arguments-part] = args + + if not name? then throw ObjectParamError "Method has no name" [ params, _ ] = function-macro-arguments-part @@ -266,8 +271,8 @@ contents = | false => "" if not params? or not is-list params - throw ObjectParamError "Expected #readable-kind-name to have a \ - parameter list" + throw ObjectParamError "Expected #readable-kind-name to have an \ + argument list" type : \Property kind : \init @@ -282,6 +287,15 @@ contents = | args.length is 0 => throw ObjectParamError "Got empty list (expected list to have contents)" + | is-atom args.0 and (args.0.value is \method) => + compile-method.call this, args[1 til] + + | is-atom args.0 and (args.0.value in <[ get set ]>) => + compile-get-set.call this, args.0.value, args[1 til] + + | args.0 `is-atom` \* => + # TODO Implement + throw ObjectParamError "Unexpected '*' (generator methods not yet implemented)" | args.length is 1 => node = args.0 @@ -300,15 +314,6 @@ contents = name : node.value shorthand : true - | is-atom args.0 and (args.0.value is \method) => - compile-method.call this, args[1 til] - - | is-atom args.0 and (args.0.value in <[ get set ]>) => - compile-get-set.call this, args.0.value, args[1 til] - - | args.0 `is-atom` \* => - # TODO Implement - throw ObjectParamError "Unexpected '*' (generator methods not yet implemented)" | otherwise # Assume a key-value pair for a normal object Property if args.length isnt 2 diff --git a/test.ls b/test.ls index ffd8494..fbb5172 100755 --- a/test.ls +++ b/test.ls @@ -894,6 +894,24 @@ test "object macro getter with arguments raises error" -> return @fail "No error thrown" +test "object macro method with no name raises error" -> + try + esl '(object (method))' + catch e + e.message `@equals` 'Unexpected object macro argument 0: Method has no name or argument list' + + return + @fail "No error thrown" + +test "object macro method with no argument list raises error" -> + try + esl '(object (method \'x))' + catch e + e.message `@equals` 'Unexpected object macro argument 0: Expected method \'x\' to have an argument list' + + return + @fail "No error thrown" + test "macro producing an object literal" -> esl "(macro obj (lambda () (return '(object ('a 1))))) (obj)"