Skip to content

Commit

Permalink
Use Enso AST in graph editor (#8123)
Browse files Browse the repository at this point in the history
The graph is now properly constructed only from the main function body, and rendered nodes are using real Enso AST.

<img width="459" alt="image" src="https://github.com/enso-org/enso/assets/919491/e0106ee7-aaea-40aa-a42b-fc91c9d8740e">
  • Loading branch information
Frizi authored Oct 24, 2023
1 parent 61a0c8c commit 10f44b5
Show file tree
Hide file tree
Showing 44 changed files with 1,450 additions and 1,303 deletions.
85 changes: 30 additions & 55 deletions app/gui2/parser-codegen/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
abstractTypeDeserializer,
abstractTypeVariants,
fieldDeserializer,
fieldDynValue,
fieldVisitor,
seekViewDyn,
support,
Expand Down Expand Up @@ -246,53 +245,6 @@ function makeReadFunction(
)
}

function makeDebugFunction(fields: Field[], typeName?: string): ts.MethodDeclaration {
const ident = tsf.createIdentifier('fields')
const fieldAssignments = fields.map((field) =>
tsf.createArrayLiteralExpression([
tsf.createStringLiteral(field.name),
fieldDynValue(field.type, field.offset),
]),
)
if (typeName != null) {
fieldAssignments.push(
tsf.createArrayLiteralExpression([
tsf.createStringLiteral('type'),
tsf.createObjectLiteralExpression([
tsf.createPropertyAssignment('type', tsf.createStringLiteral('primitive')),
tsf.createPropertyAssignment('value', tsf.createStringLiteral(typeName)),
]),
]),
)
}
return tsf.createMethodDeclaration(
[],
undefined,
ident,
undefined,
[],
[],
tsf.createTypeReferenceNode(`[string, ${support.DynValue}][]`),
tsf.createBlock([
tsf.createReturnStatement(
tsf.createArrayLiteralExpression(
[
tsf.createSpreadElement(
tsf.createCallExpression(
tsf.createPropertyAccessExpression(tsf.createSuper(), ident),
undefined,
undefined,
),
),
...fieldAssignments,
],
true,
),
),
]),
)
}

function makeVisitFunction(fields: Field[]): ts.MethodDeclaration {
const ident = tsf.createIdentifier('visitChildren')
const visitorParam = tsf.createIdentifier('visitor')
Expand Down Expand Up @@ -347,7 +299,7 @@ function makeVisitFunction(fields: Field[]): ts.MethodDeclaration {
)
}

function makeGetters(id: string, schema: Schema.Schema, typeName?: string): ts.ClassElement[] {
function makeGetters(id: string, schema: Schema.Schema): ts.ClassElement[] {
const serialization = schema.serialization[id]
const type = schema.types[id]
if (serialization == null || type == null) throw new Error(`Invalid type id: ${id}`)
Expand All @@ -360,7 +312,6 @@ function makeGetters(id: string, schema: Schema.Schema, typeName?: string): ts.C
...fields.map(makeGetter),
...fields.map(makeElementVisitor).filter((v): v is ts.ClassElement => v != null),
makeVisitFunction(fields),
makeDebugFunction(fields, typeName),
]
}

Expand Down Expand Up @@ -394,7 +345,6 @@ type ChildType = {
definition: ts.ClassDeclaration
name: ts.Identifier
enumMember: ts.EnumMember
case: ts.CaseClause
}

function makeChildType(
Expand Down Expand Up @@ -457,12 +407,11 @@ function makeChildType(
viewIdent,
tsf.createNewExpression(ident, [], [seekViewDyn(viewIdent, addressIdent)]),
),
...makeGetters(id, schema, name),
...makeGetters(id, schema),
],
),
name: tsf.createIdentifier(name),
enumMember: tsf.createEnumMember(toPascal(ty.name), discriminantInt),
case: tsf.createCaseClause(discriminantInt, [tsf.createReturnStatement(viewIdent)]),
enumMember: tsf.createEnumMember(name, discriminantInt),
}
}

Expand Down Expand Up @@ -501,6 +450,12 @@ function makeAbstractType(
'Type',
childTypes.map((child) => child.enumMember),
),
makeExportConstVariable(
'typeNames',
tsf.createArrayLiteralExpression(
childTypes.map((child) => tsf.createStringLiteralFromNode(child.name)),
),
),
...childTypes.map((child) => child.definition),
tsf.createTypeAliasDeclaration(
[modifiers.export],
Expand All @@ -522,11 +477,31 @@ function makeAbstractType(
[modifiers.export],
ident,
undefined,
tsf.createTypeReferenceNode(tsf.createQualifiedName(tsf.createIdentifier(name), name)),
tsf.createTypeReferenceNode(tsf.createQualifiedName(ident, ident)),
)
return { module: moduleDecl, export: abstractTypeExport }
}

function makeExportConstVariable(
varName: string,
initializer: ts.Expression,
): ts.VariableStatement {
return tsf.createVariableStatement(
[modifiers.export],
tsf.createVariableDeclarationList(
[
tsf.createVariableDeclaration(
varName,
undefined,
undefined,
tsf.createAsExpression(initializer, tsf.createTypeReferenceNode('const')),
),
],
ts.NodeFlags.Const,
),
)
}

function makeIsInstance(type: ts.TypeNode, baseIdent: ts.Identifier): ts.FunctionDeclaration {
const param = tsf.createIdentifier('obj')
const paramDecl = tsf.createParameterDeclaration(
Expand Down
54 changes: 2 additions & 52 deletions app/gui2/parser-codegen/serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ export const supportImports = {
ObjectVisitor: true,
ObjectAddressVisitor: true,
Result: true,
DynValue: true,
Dyn: false,
readU8: false,
readU32: false,
readI32: false,
Expand All @@ -43,8 +41,6 @@ export const support = {
DataView: tsf.createTypeReferenceNode(tsf.createIdentifier('DataView')),
Result: (t0: ts.TypeNode, t1: ts.TypeNode) =>
tsf.createTypeReferenceNode(tsf.createIdentifier('Result'), [t0, t1]),
DynValue: 'DynValue',
Dyn: tsf.createIdentifier('Dyn'),
readU8: tsf.createIdentifier('readU8'),
readU32: tsf.createIdentifier('readU32'),
readI32: tsf.createIdentifier('readI32'),
Expand Down Expand Up @@ -75,13 +71,6 @@ const baseReaders = {
readOption: readerTransformer(support.readOption),
readResult: readerTransformerTwoTyped(support.readResult),
} as const
const dynBuilders = {
Primitive: dynReader('Primitive'),
Result: dynReader('Result'),
Sequence: dynReader('Sequence'),
Option: dynReader('Option'),
Object: dynReader('Object'),
} as const

type ReadApplicator = (cursor: ts.Expression, offset: AccessOffset) => ts.Expression
type VisitorApplicator = (cursor: ts.Expression, offset: AccessOffset) => ts.Expression
Expand All @@ -91,51 +80,35 @@ type VisitorApplicator = (cursor: ts.Expression, offset: AccessOffset) => ts.Exp
export class Type {
readonly type: ts.TypeNode
readonly reader: ReadApplicator
readonly dynReader: ReadApplicator
readonly visitor: VisitorApplicator | undefined | 'visitValue'
readonly size: number

private constructor(
type: ts.TypeNode,
reader: ReadApplicator,
dynReader: ReadApplicator,
visitor: VisitorApplicator | undefined | 'visitValue',
size: number,
) {
this.type = type
this.reader = reader
this.dynReader = dynReader
this.visitor = visitor
this.size = size
}

static Abstract(name: string): Type {
const valueReader = callRead(name)
return new Type(
tsf.createTypeReferenceNode(name),
valueReader,
dynBuilders.Object(valueReader),
'visitValue',
POINTER_SIZE,
)
return new Type(tsf.createTypeReferenceNode(name), valueReader, 'visitValue', POINTER_SIZE)
}

static Concrete(name: string, size: number): Type {
const valueReader = callRead(name)
return new Type(
tsf.createTypeReferenceNode(name),
valueReader,
dynBuilders.Object(valueReader),
'visitValue',
size,
)
return new Type(tsf.createTypeReferenceNode(name), valueReader, 'visitValue', size)
}

static Sequence(element: Type): Type {
return new Type(
tsf.createTypeReferenceNode('Iterable', [element.type]),
createSequenceReader(element.size, element.reader),
dynBuilders.Sequence(createSequenceReader(element.size, element.dynReader)),
createSequenceVisitor(element.size, visitorClosure(element.visitor, element.reader)),
POINTER_SIZE,
)
Expand All @@ -145,7 +118,6 @@ export class Type {
return new Type(
tsf.createUnionTypeNode([element.type, noneType]),
baseReaders.readOption(element.reader),
dynBuilders.Option(baseReaders.readOption(element.dynReader)),
createOptionVisitor(visitorClosure(element.visitor, element.reader)),
POINTER_SIZE + 1,
)
Expand All @@ -155,7 +127,6 @@ export class Type {
return new Type(
support.Result(ok.type, err.type),
baseReaders.readResult(ok.reader, err.reader),
dynBuilders.Result(baseReaders.readResult(ok.dynReader, err.dynReader)),
createResultVisitor(
visitorClosure(ok.visitor, ok.reader),
visitorClosure(err.visitor, err.reader),
Expand All @@ -167,49 +138,42 @@ export class Type {
static Boolean: Type = new Type(
tsf.createTypeReferenceNode('boolean'),
baseReaders.readBool,
dynBuilders.Primitive(baseReaders.readBool),
undefined,
1,
)
static UInt32: Type = new Type(
tsf.createTypeReferenceNode('number'),
baseReaders.readU32,
dynBuilders.Primitive(baseReaders.readU32),
undefined,
4,
)
static Int32: Type = new Type(
tsf.createTypeReferenceNode('number'),
baseReaders.readI32,
dynBuilders.Primitive(baseReaders.readI32),
undefined,
4,
)
static UInt64: Type = new Type(
tsf.createTypeReferenceNode('bigint'),
baseReaders.readU64,
dynBuilders.Primitive(baseReaders.readU64),
undefined,
8,
)
static Int64: Type = new Type(
tsf.createTypeReferenceNode('bigint'),
baseReaders.readI64,
dynBuilders.Primitive(baseReaders.readI64),
undefined,
8,
)
static Char: Type = new Type(
tsf.createTypeReferenceNode('number'),
baseReaders.readU32,
dynBuilders.Primitive(baseReaders.readU32),
undefined,
4,
)
static String: Type = new Type(
tsf.createTypeReferenceNode('string'),
baseReaders.readString,
dynBuilders.Primitive(baseReaders.readString),
undefined,
POINTER_SIZE,
)
Expand Down Expand Up @@ -303,10 +267,6 @@ export function fieldVisitor(
)
}

export function fieldDynValue(type: Type, address: number): ts.Expression {
return type.dynReader(thisAccess(viewFieldIdent), makeConstantAddress(address))
}

function thisAccess(ident: ts.Identifier): ts.PropertyAccessExpression {
return tsf.createPropertyAccessExpression(tsf.createThis(), ident)
}
Expand Down Expand Up @@ -376,16 +336,6 @@ function readerTransformerTwoTyped(
}
}

function dynReader(name: string): (readValue: ReadApplicator) => ReadApplicator {
return (readValue) => (view, address) => {
return tsf.createCallExpression(
tsf.createPropertyAccessExpression(support.Dyn, name),
[],
[readValue(view, address)],
)
}
}

export function callRead(ident: string): ReadApplicator {
return (view, address) =>
tsf.createCallExpression(
Expand Down
8 changes: 4 additions & 4 deletions app/gui2/public/visualizations/ScatterplotVisualization.vue
Original file line number Diff line number Diff line change
Expand Up @@ -208,15 +208,15 @@ const margin = computed(() => {
}
})
const width = computed(() =>
config.value.fullscreen
config.fullscreen
? containerNode.value?.parentElement?.clientWidth ?? 0
: Math.max(config.value.width ?? 0, config.value.nodeSize.x),
: Math.max(config.width ?? 0, config.nodeSize.x),
)
const height = computed(() =>
config.value.fullscreen
config.fullscreen
? containerNode.value?.parentElement?.clientHeight ?? 0
: config.value.height ?? (config.value.nodeSize.x * 3) / 4,
: config.height ?? (config.nodeSize.x * 3) / 4,
)
const boxWidth = computed(() => Math.max(0, width.value - margin.value.left - margin.value.right))
Expand Down
17 changes: 13 additions & 4 deletions app/gui2/shared/yjsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,10 @@ export class DistributedModule {
const start = range == null ? exprStart : exprStart + range[0]
const end = range == null ? exprEnd : exprStart + range[1]
if (start > end) throw new Error('Invalid range')
if (start < exprStart || end > exprEnd) throw new Error('Range out of bounds')
if (start < exprStart || end > exprEnd)
throw new Error(
`Range out of bounds. Got [${start}, ${end}], bounds are [${exprStart}, ${exprEnd}]`,
)
this.transact(() => {
if (content.length > 0) {
this.doc.contents.insert(start, content)
Expand Down Expand Up @@ -230,7 +233,7 @@ export class IdMap {
this.finished = false
}

private static keyForRange(range: [number, number]): string {
private static keyForRange(range: readonly [number, number]): string {
return `${range[0].toString(16)}:${range[1].toString(16)}`
}

Expand All @@ -256,7 +259,12 @@ export class IdMap {
this.accessed.add(id)
}

getOrInsertUniqueId(range: [number, number]): ExprId {
getIfExist(range: readonly [number, number]): ExprId | undefined {
const key = IdMap.keyForRange(range)
return this.rangeToExpr.get(key)
}

getOrInsertUniqueId(range: readonly [number, number]): ExprId {
if (this.finished) {
throw new Error('IdMap already finished')
}
Expand Down Expand Up @@ -292,7 +300,7 @@ export class IdMap {
*
* Can be called at most once. After calling this method, the ID map is no longer usable.
*/
finishAndSynchronize(): void {
finishAndSynchronize(): typeof this.yMap {
if (this.finished) {
throw new Error('IdMap already finished')
}
Expand Down Expand Up @@ -320,6 +328,7 @@ export class IdMap {
this.yMap.set(expr, encoded)
})
})
return this.yMap
}
}

Expand Down
Loading

0 comments on commit 10f44b5

Please sign in to comment.