Skip to content

Commit

Permalink
Merge pull request #1575 from DanielXMoore/brace-unbrace
Browse files Browse the repository at this point in the history
Object and array literals via `{}` and `[]` followed by items
  • Loading branch information
edemaine authored Nov 5, 2024
2 parents 4cad99e + 2d5c003 commit f4cf992
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 6 deletions.
26 changes: 26 additions & 0 deletions civet.dev/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,19 @@ cast := {value as T}
bool := {!!available}
</Playground>

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)):

<Playground>
obj := {}
a: 1
b
person.name
method()
console.log "hi"
</Playground>

### Property Names

Both braced and unbraced literals support shorthand for
Expand Down Expand Up @@ -338,6 +351,19 @@ people := [
]
</Playground>

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)):

<Playground>
rotate := []
c, -s
s, c
func.apply @, []
arg1
arg2
</Playground>

### Bulleted

Instead of brackets, array items can be specified via `.` or `` bullets:
Expand Down
60 changes: 54 additions & 6 deletions source/parser.hera
Original file line number Diff line number Diff line change
Expand Up @@ -2977,6 +2977,28 @@ ArrayLiteral
_ArrayLiteral
# NOTE: Check ArrayBindingPattern case of lhs destructuring
ArrayBindingPattern UpcomingAssignment -> $1
# 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],
names: content.flatMap((c) => c?.names || [])
}
OpenBracket:open AllowAll ( ArrayLiteralContent __ CloseBracket )? RestoreAll ->
if (!$3) return $skip
const [ content, ws, close ] = $3
Expand Down Expand Up @@ -3133,6 +3155,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]
Expand Down Expand Up @@ -3296,6 +3321,24 @@ ObjectLiteral
InlineObjectLiteral

BracedObjectLiteral
# 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) {
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
Expand All @@ -3307,11 +3350,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]
Expand All @@ -3323,6 +3365,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 )+ ->
Expand Down Expand Up @@ -3427,7 +3474,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
Expand Down
61 changes: 61 additions & 0 deletions test/array.civet
Original file line number Diff line number Diff line change
Expand Up @@ -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])
"""
86 changes: 86 additions & 0 deletions test/object.civet
Original file line number Diff line number Diff line change
Expand Up @@ -1444,3 +1444,89 @@ 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})
"""

testCase """
single-line
---
{} a, b: 2
f {} a, b: 2
---
({ a, b: 2})
f({ a, b: 2})
"""

0 comments on commit f4cf992

Please sign in to comment.