Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Object and array literals via {} and [] followed by items #1575

Merged
merged 5 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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})
"""
Loading