From 2da32c201b68309f96fd6d8dec94bbeda80e9c24 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Mon, 4 Nov 2024 17:35:26 -0500 Subject: [PATCH 1/5] Object literals via `{}` followed by indented properties --- source/parser.hera | 20 +++++++++++- test/object.civet | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/source/parser.hera b/source/parser.hera index 7df0c6d6..67c091d5 100644 --- a/source/parser.hera +++ b/source/parser.hera @@ -3296,6 +3296,23 @@ ObjectLiteral InlineObjectLiteral BracedObjectLiteral + OpenBrace:open CloseBrace:close AllowAll NestedPropertyDefinitions?:properties RestoreAll -> + if (!properties) return $skip + let last = properties[properties.length - 1] + if (last.delim?.implicit) { + last = { + ...last, + delim: undefined, + children: last.children.filter(c => c !== last.delim), + } + properties = [ ...properties.slice(0, properties.length - 1), last ] + } + return { + type: "ObjectExpression", + children: [open, properties, close], + names: properties.flatMap((c) => c.names || []), + properties, + } OpenBrace:open AllowAll ( BracedObjectLiteralContent __ CloseBrace )? RestoreAll -> if (!$3) return $skip const [ properties, ...close ] = $3 @@ -3427,7 +3444,8 @@ ObjectPropertyDelimiter _? Comma # Object closing delimits the property &( __ "}" ) - &EOS InsertComma -> $2 + &EOS InsertComma -> + return { ...$2, implicit: true } # https://262.ecma-international.org/#prod-PropertyDefinition # NOTE: Must start on same line diff --git a/test/object.civet b/test/object.civet index 88cb2316..1b5fe905 100644 --- a/test/object.civet +++ b/test/object.civet @@ -1444,3 +1444,79 @@ describe "object", -> --- ({interface:state.interface,is:state.is,let:state.let,loop:state.loop,new:state.new,not:state.not,or:state.or,private:state.private,protected:state.protected,public:state.public,return:state.return,static:state.static,super:state.super,switch:state.switch,throw:state.throw,try:state.try,typeof:state.typeof,unless:state.unless,until:state.until,var:state.var,void:state.void,while:state.while,with:state.with,yield:state.yield}) """ + + describe "{} with nested properties", -> + testCase """ + names + --- + {} + a + b + c + --- + ({ + a, + b, + c}) + """ + + testCase """ + properties + --- + {} + a: 1 + b: 2 + c: 3 + --- + ({ + a: 1, + b: 2, + c: 3}) + """ + + testCase """ + methods + --- + {} + a() 1 + b() + 2 + --- + ({ + a() { return 1 }, + b() { + return 2 + }}) + """ + + testCase """ + mixture + --- + {} + a + b: 2 + c() 3 + --- + ({ + a, + b: 2, + c() { return 3 }}) + """ + + testCase """ + function call + --- + f {} + a + b: 2 + f x, {} + a + b: 2 + --- + f({ + a, + b: 2}) + f(x, { + a, + b: 2}) + """ From f31852796b26b2efe0a5694741d390ca72b792b3 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Mon, 4 Nov 2024 17:43:34 -0500 Subject: [PATCH 2/5] Same-line `{}` objects --- source/parser.hera | 18 +++++++++++------- test/object.civet | 10 ++++++++++ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/source/parser.hera b/source/parser.hera index 67c091d5..465b306c 100644 --- a/source/parser.hera +++ b/source/parser.hera @@ -3296,8 +3296,8 @@ ObjectLiteral InlineObjectLiteral BracedObjectLiteral - OpenBrace:open CloseBrace:close AllowAll NestedPropertyDefinitions?:properties RestoreAll -> - if (!properties) return $skip + OpenBrace:open CloseBrace:close AllowAll ( NestedPropertyDefinitions / SingleLineObjectProperties )?:properties RestoreAll -> + if (!properties?.length) return $skip let last = properties[properties.length - 1] if (last.delim?.implicit) { last = { @@ -3324,11 +3324,10 @@ BracedObjectLiteral properties, } -BracedObjectLiteralContent - # Allow unnested comma-separated properties on first line, optionally - # followed by nested comma-separated properties on further lines - ( PropertyDefinition ObjectPropertyDelimiter )*:line NestedPropertyDefinitions?:nested -> - line = line.flatMap(([prop, delim]) => { +# Comma-separted properties on a single line. Allows for zero properties. +SingleLineObjectProperties + ( PropertyDefinition ObjectPropertyDelimiter )*:line -> + return line.flatMap(([prop, delim]) => { // Allow each prop to be a single Property object or an array of such prop = Array.isArray(prop) ? prop : [prop] let last = prop[prop.length-1] @@ -3340,6 +3339,11 @@ BracedObjectLiteralContent } return [...prop.slice(0, prop.length-1), last] }) + +BracedObjectLiteralContent + # Allow unnested comma-separated properties on first line, optionally + # followed by nested comma-separated properties on further lines + SingleLineObjectProperties:line NestedPropertyDefinitions?:nested -> return line.concat(nested || []) # As a backup, allow for arbitrary untracked indentation ( __ PropertyDefinition ObjectPropertyDelimiter )+ -> diff --git a/test/object.civet b/test/object.civet index 1b5fe905..f335d83b 100644 --- a/test/object.civet +++ b/test/object.civet @@ -1520,3 +1520,13 @@ describe "object", -> a, b: 2}) """ + + testCase """ + single-line + --- + {} a, b: 2 + f {} a, b: 2 + --- + ({ a, b: 2}) + f({ a, b: 2}) + """ From 0cf37b038ef51629738f253ddea6720b561eed1d Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Mon, 4 Nov 2024 18:09:18 -0500 Subject: [PATCH 3/5] Array literals via `[]` followed by elements --- source/parser.hera | 10 ++++++++ test/array.civet | 61 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/source/parser.hera b/source/parser.hera index 465b306c..e4f4f5a1 100644 --- a/source/parser.hera +++ b/source/parser.hera @@ -2977,6 +2977,13 @@ ArrayLiteral _ArrayLiteral # NOTE: Check ArrayBindingPattern case of lhs destructuring ArrayBindingPattern UpcomingAssignment -> $1 + OpenBracket:open CloseBracket:close AllowAll ( NestedElementList / SingleLineElementList )?:content RestoreAll -> + if (!content) return $skip + return { + type: "ArrayExpression", + children: [open, ...content, close], + names: content.flatMap((c) => c?.names || []) + } OpenBracket:open AllowAll ( ArrayLiteralContent __ CloseBracket )? RestoreAll -> if (!$3) return $skip const [ content, ws, close ] = $3 @@ -3133,6 +3140,9 @@ ElementListWithIndentedApplicationForbidden # NOTE: Modified and simplified from the spec ElementList BulletedArray -> [$1] + SingleLineElementList + +SingleLineElementList # NOTE: Forbid EOS to prevent eating indentation in Expression !EOS ArrayElementExpression:first ElementListRest*:rest -> if (!rest.length) return [first] diff --git a/test/array.civet b/test/array.civet index 90d761b6..5a3406d2 100644 --- a/test/array.civet +++ b/test/array.civet @@ -840,3 +840,64 @@ describe "array", -> .length .toString()) """ + + describe "[] followed by elements", -> + testCase """ + indented + --- + [] + 1 + b + c + --- + [ + 1, + b, + c] + """ + + testCase """ + with implicit object + --- + [] + a: 1 + b: 2 + c: 3 + --- + [{ + a: 1, + b: 2, + c: 3}] + """ + + testCase """ + function call + --- + f [] + 1 + b + c + f x, [] + 1 + b + c + --- + f([ + 1, + b, + c]) + f(x, [ + 1, + b, + c]) + """ + + testCase """ + single-line + --- + [] 1, b, c + f [] 1, b, c + --- + [ 1, b, c] + f([ 1, b, c]) + """ From 2e4cc6c4d0f9cbb62a3f508581f2710a6b99b458 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Mon, 4 Nov 2024 21:34:33 -0500 Subject: [PATCH 4/5] Fix `{}` and `[]` to apply only in application context --- source/parser.hera | 20 ++++++++++++++++++-- test/array.civet | 4 ++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/source/parser.hera b/source/parser.hera index e4f4f5a1..26e736d5 100644 --- a/source/parser.hera +++ b/source/parser.hera @@ -2977,8 +2977,23 @@ ArrayLiteral _ArrayLiteral # NOTE: Check ArrayBindingPattern case of lhs destructuring ArrayBindingPattern UpcomingAssignment -> $1 - OpenBracket:open CloseBracket:close AllowAll ( NestedElementList / SingleLineElementList )?:content RestoreAll -> + # NOTE: Added [] followed by elements + OpenBracket:open CloseBracket:close ApplicationStart AllowAll ( NestedElementList / SingleLineElementList )?:content RestoreAll -> if (!content) return $skip + + // Remove trailing comma + let last = content[content.length - 1] + let lastArray = Array.isArray(last) ? last : last.children + if (isComma(lastArray[lastArray.length - 1])) { + lastArray = lastArray.slice(0, -1) + if (Array.isArray(last)) { + last = lastArray + } else { + last = { ...last, children: lastArray } + } + content = [ ...content.slice(0, -1), last ] + } + return { type: "ArrayExpression", children: [open, ...content, close], @@ -3306,7 +3321,8 @@ ObjectLiteral InlineObjectLiteral BracedObjectLiteral - OpenBrace:open CloseBrace:close AllowAll ( NestedPropertyDefinitions / SingleLineObjectProperties )?:properties RestoreAll -> + # NOTE: Added {} followed by properties + OpenBrace:open CloseBrace:close ApplicationStart AllowAll ( NestedPropertyDefinitions / SingleLineObjectProperties )?:properties RestoreAll -> if (!properties?.length) return $skip let last = properties[properties.length - 1] if (last.delim?.implicit) { diff --git a/test/array.civet b/test/array.civet index 5a3406d2..ab877c7a 100644 --- a/test/array.civet +++ b/test/array.civet @@ -864,8 +864,8 @@ describe "array", -> b: 2 c: 3 --- - [{ - a: 1, + [ + {a: 1, b: 2, c: 3}] """ From 2d5c003468343f3eec5363d5512ca8dd695b1463 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Mon, 4 Nov 2024 21:43:57 -0500 Subject: [PATCH 5/5] Document `{}` and `[]` --- civet.dev/reference.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/civet.dev/reference.md b/civet.dev/reference.md index 04460aeb..b2f1e4ba 100644 --- a/civet.dev/reference.md +++ b/civet.dev/reference.md @@ -165,6 +165,19 @@ cast := {value as T} bool := {!!available} +To avoid the trailing `}` in a braced object literal, you can use `{}` +followed by its properties (as if they were arguments +in an [implicit function application](#function-calls)): + + +obj := {} + a: 1 + b + person.name + method() + console.log "hi" + + ### Property Names Both braced and unbraced literals support shorthand for @@ -338,6 +351,19 @@ people := [ ] +To avoid the trailing `]` in a bracketed array literal, you can use `[]` +followed by its elements (as if they were arguments +in an [implicit function application](#function-calls)): + + +rotate := [] + c, -s + s, c +func.apply @, [] + arg1 + arg2 + + ### Bulleted Instead of brackets, array items can be specified via `.` or `•` bullets: