Skip to content

Commit 3390491

Browse files
authored
Move annotations into fields of Function and ConstructorDefinition (#11374)
Move annotations into fields of Function and ConstructorDefinition. # Important Notes New syntax: Constructor argument-definition lines - Each argument in a type-constructor definition may be specified on its own (indented) line. Relaxed syntax: Unparenthesized arguments to annotations - A generic annotation now uses the rest of the line as its argument expression; the expression no longer needs to be parenthesized.
1 parent a74b9e3 commit 3390491

File tree

18 files changed

+599
-240
lines changed

18 files changed

+599
-240
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
[11235]: https://github.com/enso-org/enso/pull/11235
2727
[11255]: https://github.com/enso-org/enso/pull/11255
2828

29+
#### Enso Language & Runtime
30+
31+
- [Arguments in constructor definitions may now be on their own lines][11374]
32+
33+
[11374]: https://github.com/enso-org/enso/pull/11374
34+
2935
# Enso 2024.4
3036

3137
#### Enso IDE

app/ydoc-shared/src/ast/parse.ts

+22-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import * as map from 'lib0/map'
2-
import type { AstId, Module, NodeChild, Owned, OwnedRefs, TextElement, TextToken } from '.'
2+
import type {
3+
AstId,
4+
FunctionFields,
5+
Module,
6+
NodeChild,
7+
Owned,
8+
OwnedRefs,
9+
TextElement,
10+
TextToken,
11+
} from '.'
312
import {
413
Token,
514
asOwned,
@@ -166,12 +175,20 @@ class Abstractor {
166175
break
167176
}
168177
case RawAst.Tree.Type.Function: {
169-
const name = this.abstractTree(tree.name)
178+
const annotationLines = Array.from(tree.annotationLines, anno => ({
179+
annotation: {
180+
operator: this.abstractToken(anno.annotation.operator),
181+
annotation: this.abstractToken(anno.annotation.annotation),
182+
argument: anno.annotation.argument && this.abstractTree(anno.annotation.argument),
183+
},
184+
newlines: Array.from(anno.newlines, this.abstractToken.bind(this)),
185+
}))
170186
const signatureLine = tree.signatureLine && {
171187
signature: this.abstractTypeSignature(tree.signatureLine.signature),
172188
newlines: Array.from(tree.signatureLine.newlines, this.abstractToken.bind(this)),
173189
}
174190
const private_ = tree.private && this.abstractToken(tree.private)
191+
const name = this.abstractTree(tree.name)
175192
const argumentDefinitions = Array.from(tree.args, arg => ({
176193
open: arg.open && this.abstractToken(arg.open),
177194
open2: arg.open2 && this.abstractToken(arg.open2),
@@ -190,15 +207,15 @@ class Abstractor {
190207
}))
191208
const equals = this.abstractToken(tree.equals)
192209
const body = tree.body !== undefined ? this.abstractTree(tree.body) : undefined
193-
node = Function.concrete(
194-
this.module,
210+
node = Function.concrete(this.module, {
211+
annotationLines,
195212
signatureLine,
196213
private_,
197214
name,
198215
argumentDefinitions,
199216
equals,
200217
body,
201-
)
218+
} satisfies FunctionFields<OwnedRefs>)
202219
break
203220
}
204221
case RawAst.Tree.Type.Ident: {

app/ydoc-shared/src/ast/tree.ts

+53-30
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ type StructuralField<T extends TreeRefs = RawRefs> =
451451
| VectorElement<T>
452452
| TypeSignature<T>
453453
| SignatureLine<T>
454+
| FunctionAnnotation<T>
455+
| AnnotationLine<T>
454456

455457
/** Type whose fields are all suitable for storage as `Ast` fields. */
456458
interface FieldObject<T extends TreeRefs> {
@@ -568,6 +570,10 @@ function mapRefs<T extends TreeRefs, U extends TreeRefs>(
568570
field: VectorElement<T>,
569571
f: MapRef<T, U>,
570572
): VectorElement<U>
573+
function mapRefs<T extends TreeRefs, U extends TreeRefs>(
574+
field: AnnotationLine<T>,
575+
f: MapRef<T, U>,
576+
): AnnotationLine<U>
571577
function mapRefs<T extends TreeRefs, U extends TreeRefs>(
572578
field: SignatureLine<T>,
573579
f: MapRef<T, U>,
@@ -2035,6 +2041,17 @@ interface ArgumentType<T extends TreeRefs = RawRefs> {
20352041
type: T['ast']
20362042
}
20372043

2044+
interface FunctionAnnotation<T extends TreeRefs = RawRefs> {
2045+
operator: T['token']
2046+
annotation: T['token']
2047+
argument: T['ast'] | undefined
2048+
}
2049+
2050+
interface AnnotationLine<T extends TreeRefs = RawRefs> {
2051+
annotation: FunctionAnnotation<T>
2052+
newlines: T['token'][]
2053+
}
2054+
20382055
interface TypeSignature<T extends TreeRefs = RawRefs> {
20392056
name: T['ast']
20402057
operator: T['token']
@@ -2046,13 +2063,14 @@ interface SignatureLine<T extends TreeRefs = RawRefs> {
20462063
newlines: T['token'][]
20472064
}
20482065

2049-
export interface FunctionFields {
2050-
signatureLine: SignatureLine | undefined
2051-
private_: NodeChild<SyncTokenId> | undefined
2052-
name: NodeChild<AstId>
2053-
argumentDefinitions: ArgumentDefinition[]
2054-
equals: NodeChild<SyncTokenId>
2055-
body: NodeChild<AstId> | undefined
2066+
export interface FunctionFields<T extends TreeRefs = RawRefs> {
2067+
annotationLines: AnnotationLine<T>[]
2068+
signatureLine: SignatureLine<T> | undefined
2069+
private_: T['token'] | undefined
2070+
name: T['ast']
2071+
argumentDefinitions: ArgumentDefinition<T>[]
2072+
equals: T['token']
2073+
body: T['ast'] | undefined
20562074
}
20572075
/** TODO: Add docs */
20582076
export class Function extends Ast {
@@ -2086,24 +2104,24 @@ export class Function extends Ast {
20862104
/** TODO: Add docs */
20872105
static concrete(
20882106
module: MutableModule,
2089-
signatureLine: SignatureLine<OwnedRefs> | undefined,
2090-
private_: NodeChild<Token> | undefined,
2091-
name: NodeChild<Owned>,
2092-
argumentDefinitions: ArgumentDefinition<OwnedRefs>[],
2093-
equals: NodeChild<Token>,
2094-
body: NodeChild<Owned> | undefined,
2107+
fields: Partial<FunctionFields<OwnedRefs>> & { name: object } & { equals: object },
20952108
) {
20962109
const base = module.baseObject('Function')
20972110
const id_ = base.get('id')
2098-
const fields = composeFieldData(base, {
2099-
signatureLine: signatureLine && mapRefs(signatureLine, ownedToRaw(module, id_)),
2100-
private_,
2101-
name: concreteChild(module, name, id_),
2102-
argumentDefinitions: argumentDefinitions.map(def => mapRefs(def, ownedToRaw(module, id_))),
2103-
equals,
2104-
body: concreteChild(module, body, id_),
2111+
const rawFields = composeFieldData(base, {
2112+
annotationLines: (fields.annotationLines ?? []).map(anno =>
2113+
mapRefs(anno, ownedToRaw(module, id_)),
2114+
),
2115+
signatureLine: fields.signatureLine && mapRefs(fields.signatureLine, ownedToRaw(module, id_)),
2116+
private_: fields.private_,
2117+
name: concreteChild(module, fields.name, id_),
2118+
argumentDefinitions: (fields.argumentDefinitions ?? []).map(def =>
2119+
mapRefs(def, ownedToRaw(module, id_)),
2120+
),
2121+
equals: fields.equals,
2122+
body: concreteChild(module, fields.body, id_),
21052123
})
2106-
return asOwned(new MutableFunction(module, fields))
2124+
return asOwned(new MutableFunction(module, rawFields))
21072125
}
21082126

21092127
/** TODO: Add docs */
@@ -2116,15 +2134,12 @@ export class Function extends Ast {
21162134
// Note that a function name may not be an operator if the function is not in the body of a type definition, but we
21172135
// can't easily enforce that because we don't currently make a syntactic distinction between top-level functions and
21182136
// type methods.
2119-
return MutableFunction.concrete(
2120-
module,
2121-
undefined,
2122-
undefined,
2123-
unspaced(Ident.newAllowingOperators(module, name)),
2137+
return MutableFunction.concrete(module, {
2138+
name: unspaced(Ident.newAllowingOperators(module, name)),
21242139
argumentDefinitions,
2125-
spaced(makeEquals()),
2126-
autospaced(body),
2127-
)
2140+
equals: spaced(makeEquals()),
2141+
body: autospaced(body),
2142+
})
21282143
}
21292144

21302145
/** Construct a function with simple (name-only) arguments and a body block. */
@@ -2161,7 +2176,15 @@ export class Function extends Ast {
21612176

21622177
/** TODO: Add docs */
21632178
*concreteChildren(_verbatim?: boolean): IterableIterator<RawNodeChild> {
2164-
const { signatureLine, private_, name, argumentDefinitions, equals, body } = getAll(this.fields)
2179+
const { annotationLines, signatureLine, private_, name, argumentDefinitions, equals, body } =
2180+
getAll(this.fields)
2181+
for (const anno of annotationLines) {
2182+
const { operator, annotation, argument } = anno.annotation
2183+
yield operator
2184+
yield annotation
2185+
if (argument) yield argument
2186+
yield* anno.newlines
2187+
}
21652188
if (signatureLine) {
21662189
const { signature, newlines } = signatureLine
21672190
const { name, operator, type } = signature

distribution/lib/Standard/Database/0.0.0-dev/src/Connection/Postgres.enso

+8-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,14 @@ type Postgres
3030
- use_ssl: Whether to use SSL (defaults to `SSL_Mode.Prefer`).
3131
- client_cert: The client certificate to use or `Nothing` if not needed.
3232
@credentials Credentials.default_widget
33-
Server (host:Text=default_postgres_host) (port:Integer=default_postgres_port) (database:Text=default_postgres_database) (schema:Text="") (credentials:(Credentials|Nothing)=Nothing) (use_ssl:SSL_Mode=SSL_Mode.Prefer) (client_cert:(Client_Certificate|Nothing)=Nothing)
33+
Server
34+
host : Text = default_postgres_host
35+
port : Integer = default_postgres_port
36+
database : Text = default_postgres_database
37+
schema : Text = ""
38+
credentials : Credentials|Nothing = Nothing
39+
use_ssl : SSL_Mode = SSL_Mode.Prefer
40+
client_cert : Client_Certificate|Nothing = Nothing
3441

3542
## PRIVATE
3643
Attempt to resolve the constructor.

distribution/lib/Standard/Table/0.0.0-dev/src/Data_Formatter.enso

+6-6
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ type Data_Formatter
5959
- time_formats: Expected time formats.
6060
- true_values: Values representing True.
6161
- false_values: Values representing False.
62-
@datetime_formats (make_vector_widget make_date_time_format_selector)
63-
@date_formats (make_vector_widget make_date_format_selector)
64-
@time_formats (make_vector_widget make_time_format_selector)
62+
@datetime_formats make_vector_widget make_date_time_format_selector
63+
@date_formats make_vector_widget make_date_format_selector
64+
@time_formats make_vector_widget make_time_format_selector
6565
Value trim_values:Boolean=True allow_leading_zeros:Boolean=False decimal_point:Text|Auto=Auto thousand_separator:Text='' allow_exponential_notation:Boolean=False datetime_formats:(Vector Date_Time_Formatter)=[Date_Time_Formatter.default_enso_zoned_date_time] date_formats:(Vector Date_Time_Formatter)=[Date_Time_Formatter.iso_date] time_formats:(Vector Text)=[Date_Time_Formatter.iso_time] true_values:(Vector Text)=["True","true","TRUE"] false_values:(Vector Text)=["False","false","FALSE"]
6666

6767
## PRIVATE
@@ -120,9 +120,9 @@ type Data_Formatter
120120
- datetime_formats: Expected datetime formats.
121121
- date_formats: Expected date formats.
122122
- time_formats: Expected time formats.
123-
@datetime_formats (make_vector_widget make_date_time_format_selector)
124-
@date_formats (make_vector_widget make_date_format_selector)
125-
@time_formats (make_vector_widget make_time_format_selector)
123+
@datetime_formats make_vector_widget make_date_time_format_selector
124+
@date_formats make_vector_widget make_date_format_selector
125+
@time_formats make_vector_widget make_time_format_selector
126126
with_datetime_formats : (Vector Date_Time_Formatter | Date_Time_Formatter) -> (Vector Date_Time_Formatter | Date_Time_Formatter) -> (Vector Date_Time_Formatter | Date_Time_Formatter) -> Data_Formatter
127127
with_datetime_formats self datetime_formats:(Vector | Date_Time_Formatter)=self.datetime_formats date_formats:(Vector | Date_Time_Formatter)=self.date_formats time_formats:(Vector | Date_Time_Formatter)=self.time_formats =
128128
convert_formats formats =

docs/syntax/types.md

+12
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,18 @@ below.
297297
fully support currying - e.g. one can create `fn = Maybe.Just` and later apply
298298
two to it (`fn 2`) to obtain new atom.
299299

300+
Constructor arguments may be defined on the same line as above (similarly to
301+
the syntax for defining functions); alternatively, each argument may be
302+
defined on its own line in an indented block following the constructor name.
303+
This can make complex constructor definitions easier to read:
304+
305+
```ruby
306+
type Maybe
307+
Nothing
308+
Just
309+
value : Integer
310+
```
311+
300312
- **Autoscoped Constructors:** Referencing constructors via their type name may
301313
lead to long and boilerplate code. To simplify referencing constructors when
302314
the _context is known_ a special `..` syntax is supported. Should there be a

engine/runtime-integration-tests/src/test/java/org/enso/compiler/test/ErrorCompilerTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ public void unfinishedLiteral1() throws Exception {
2525
}
2626

2727
@Test
28-
public void brokenAnnotation() throws Exception {
28+
public void brokenAnnotationMissingArgument() throws Exception {
2929
var ir = parse("""
3030
@anno
3131
fn = 10
3232
""");
3333

3434
assertSingleSyntaxError(
35-
ir, Syntax.UnexpectedExpression$.MODULE$, "Unexpected expression", 0, 13);
35+
ir, Syntax.UnexpectedExpression$.MODULE$, "Unexpected expression", 0, 5);
3636
}
3737

3838
@Test

engine/runtime-integration-tests/src/test/scala/org/enso/compiler/test/pass/resolve/ModuleAnnotationsTest.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.enso.compiler.core.ir.Function
77
import org.enso.compiler.core.ir.Module
88
import org.enso.compiler.core.ir.Name
99
import org.enso.compiler.core.ir.expression.Comment
10+
import org.enso.compiler.core.ir.expression.errors
1011
import org.enso.compiler.core.ir.module.scope.Definition
1112
import org.enso.compiler.core.ir.module.scope.definition
1213
import org.enso.compiler.pass.{PassConfiguration, PassGroup, PassManager}
@@ -184,7 +185,7 @@ class ModuleAnnotationsTest extends CompilerTest {
184185
ir.bindings.head shouldBe a[Definition.SugaredType]
185186
val typ = ir.bindings.head.asInstanceOf[Definition.SugaredType]
186187
typ.body.length shouldEqual 3
187-
typ.body(0) shouldBe an[Name.GenericAnnotation]
188+
typ.body(0) shouldBe an[errors.Syntax]
188189
typ.body(1) shouldBe an[Comment]
189190
typ.body(2) shouldBe a[Definition.Data]
190191
}

engine/runtime-integration-tests/src/test/scala/org/enso/interpreter/test/semantic/ConstructorsTest.scala

+25
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,30 @@ class ConstructorsTest extends InterpreterTest {
104104
|""".stripMargin
105105
eval(testCode) shouldEqual 55
106106
}
107+
108+
"support argument-definition lines" in {
109+
val testCode =
110+
"""import Standard.Base.Nothing.Nothing
111+
|import Standard.Base.Any.Any
112+
|
113+
|type C2
114+
| Cons2
115+
| # Each argument may be specified on its own indented line.
116+
| a
117+
| # Blank lines (or plain comments) are allowed between arguments.
118+
| b
119+
|
120+
|Nothing.genList = i -> if i == 0 then Nil2 else C2.Cons2 i (Nothing.genList (i - 1))
121+
|
122+
|type Nil2
123+
|
124+
|Nothing.sumList = list -> case list of
125+
| C2.Cons2 h t -> h + Nothing.sumList t
126+
| Nil2 -> 0
127+
|
128+
|main = Nothing.sumList (Nothing.genList 10)
129+
|""".stripMargin
130+
eval(testCode) shouldEqual 55
131+
}
107132
}
108133
}

0 commit comments

Comments
 (0)