From 5383b4572c70d1d227b01a4fd843d53ced57e428 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Fri, 13 Mar 2026 15:34:35 +0100 Subject: [PATCH 01/11] Use parse results reactively in AnnotationParseResults template --- .../annotation-parse-results.component.html | 12 +++------ .../annotation-parse-results.component.ts | 25 ++++++++----------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html index 7ea3699b..df82c85c 100644 --- a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html +++ b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html @@ -1,21 +1,17 @@ +@let ccgTrees = (parseResults$ | async)?.ccg_trees;
-

Parser: {{ parseResults.parser }}

- @for (sentence of parseResults.sentences; track sentence.id) { + @for (tree of ccgTrees; track $index) {

-
- @for (parse of sentence.parses; track $index) { - - } -
+
diff --git a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts index 86b635a0..dfe42421 100644 --- a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts +++ b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts @@ -1,31 +1,26 @@ -import { Component, DestroyRef, inject, OnInit } from "@angular/core"; +import { Component, DestroyRef, inject } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; -import { mockResult } from "./mockParseResult"; import { ParseService } from "@/services/parse.service"; import { ParseTreeTableComponent } from "./parse-tree-table/parse-tree-table.component"; import { NgbAccordionModule } from "@ng-bootstrap/ng-bootstrap"; +import { map } from "rxjs"; +import { CommonModule } from "@angular/common"; @Component({ selector: "la-annotation-parse-results", standalone: true, - imports: [ParseTreeTableComponent, NgbAccordionModule], + imports: [ParseTreeTableComponent, NgbAccordionModule, CommonModule], templateUrl: "./annotation-parse-results.component.html", styleUrl: "./annotation-parse-results.component.scss", }) -export class AnnotationParseResultsComponent implements OnInit { +export class AnnotationParseResultsComponent { private destroyRef = inject(DestroyRef); private parseService = inject(ParseService); - public parseResults = mockResult; - - ngOnInit(): void { - // Subscription needed to ensure a request is actually made. - this.parseService.parse$ - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((response) => { - console.log("Parse response:", response); - this.parseResults = response.data.ccg_trees; - }); - } + public parseResults$ = this.parseService.parse$ + .pipe( + map(response => response?.data || null), + takeUntilDestroyed(this.destroyRef) + ); } From 37753686b53f035d7929f042b5ef23c1d1c62681 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Fri, 13 Mar 2026 20:01:49 +0100 Subject: [PATCH 02/11] Handle single CCG parse tree --- .../parse-tree-table.component.html | 4 +- .../parse-tree-table.component.ts | 168 ++++++++++-------- .../annotation-parse-results/types.ts | 69 ++----- frontend/src/app/services/parse.service.ts | 11 +- 4 files changed, 124 insertions(+), 128 deletions(-) diff --git a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.html b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.html index 68acf091..215c3b28 100644 --- a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.html +++ b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.html @@ -1,10 +1,10 @@
-

{{ treeType() }}

+

--tree type--

- +
diff --git a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts index b9b8f69f..0eb666ea 100644 --- a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts +++ b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts @@ -1,87 +1,113 @@ import { Component, computed, input } from '@angular/core'; -import { BinaryNode, LeafNode, ParseTree, ParseTreeNode, ParseTreeType, UnaryNode, VariableNode } from '../types'; -import { TreeNodeComponent, TreeNodeDisplay } from './tree-node.component'; +import { TreeNodeComponent } from './tree-node.component'; +import { CCGNode, LeafNode, BinaryNode, UnaryNode } from "../types"; +import { TreeNodeDisplay } from "./tree-node.component"; -const TreeTypeDisplay: Record = { - [ParseTreeType.CCG_DERIVATION]: 'CCG Derivation', - [ParseTreeType.CCG_TERM]: 'CCG Term', - [ParseTreeType.CORRECTED_CCG_TERM]: 'Corrected CCG Term', - [ParseTreeType.FIRST_LLF]: 'First LLF' -}; - - -@Component({ - selector: 'la-parse-tree-table', - imports: [TreeNodeComponent], - templateUrl: './parse-tree-table.component.html', - styleUrl: './parse-tree-table.component.scss' -}) -export class ParseTreeTableComponent { - public readonly tree = input.required(); - - public rootNode = computed(() => this.buildDisplayTree(this.tree().root)); - - public treeType = computed(() => TreeTypeDisplay[this.tree().type] || "Unknown Type"); - - private buildDisplayTree(node: ParseTreeNode): TreeNodeDisplay { - switch (node.type) { - case 'leaf': - return this.buildLeafNode(node); - case 'binary': - return this.buildBinaryNode(node); - case 'unary': - return this.buildUnaryNode(node); - case 'var': - return this.buildVariableNode(node); - } - } - - private buildLeafNode(node: LeafNode): TreeNodeDisplay { +export function buildDisplayTree(node: CCGNode): TreeNodeDisplay { + if (nodeIsLeaf(node)) { + return buildLeafNode(node); + } else if (nodeIsBinary(node)) { + return buildBinaryNode(node); + } else if (nodeIsUnary(node)) { + return buildUnaryNode(node); + } else { return { - type: 'leaf', - content: node.cat, children: [], - leaf: { - tok: node.tok, - lem: node.lem, - pos: node.pos, - ner: node.ner - } + content: "Unknown Node Type", + type: 'node' }; } +} - private buildVariableNode(node: VariableNode): TreeNodeDisplay { - return { - type: 'var', - content: node.name, - children: [], - var: { - typeInfo: node.typeInfo - } - }; - } +function nodeIsLeaf(node: CCGNode): node is LeafNode { + return Array.isArray(node.node); +} - private buildBinaryNode(node: BinaryNode): TreeNodeDisplay { - const left = this.buildDisplayTree(node.left); - const right = this.buildDisplayTree(node.right); +function nodeIsBinary(node: CCGNode): node is BinaryNode { + return 'children' in node && node.children.length === 2; +} - return { - type: 'node', - content: node.cat, - rule: node.rule, - children: [left, right] - }; - } +function nodeIsUnary(node: CCGNode): node is UnaryNode { + return 'children' in node && node.children.length === 1; +} - private buildUnaryNode(node: UnaryNode): TreeNodeDisplay { - const child = this.buildDisplayTree(node.child); +function buildLeafNode(node: LeafNode): TreeNodeDisplay { + const [_rule, tok, lem, pos, ner, cat] = node.node; + return { + type: 'leaf', + content: cat, + children: [], + leaf: { tok, lem, pos, ner } + }; +} + +function buildBinaryNode(node: BinaryNode): TreeNodeDisplay { + const left = buildDisplayTree(node.children[0]); + const right = buildDisplayTree(node.children[1]); + + const { content, rule } = extractRule(node.node); + + return { + type: 'node', + content: content, + rule: rule, + children: [left, right] + }; +} + +function buildUnaryNode(node: UnaryNode): TreeNodeDisplay { + const child = buildDisplayTree(node.children[0]); + const { content, rule } = extractRule(node.node); + + return { + type: 'node', + content: content, + rule: rule, + children: [child] + }; +} + +/** + * Parses a node string to extract the rule and the content. + * + * A node string is usually of the form "A(B)", where a is the rule applied + * and B is the resulting category. The rule is anything everything before + * the first parenthesis. Everything within it is the content. For example, + * in "fa(s:ng-np)", "fa" is the rule and "s:ng-np" is the content. + * + * Due to a bug in the CCG parser, sometimes the node string can have + * multiple layers of parentheses, e.g. fa(((s:ng-np)-(s:ng-np))). + * function only strips off the first. + * + */ +function extractRule(nodeString: string): { rule: string, content: string; } { + const firstParen = nodeString.indexOf('('); + const lastParen = nodeString.lastIndexOf(')'); + + // Return a fallback value if the string is not what we expect. + if (firstParen === -1 || lastParen === -1 || lastParen < firstParen) { return { - type: 'node', - content: node.cat, - rule: node.rule, - children: [child] + rule: "", + content: nodeString }; } + const rule = nodeString.slice(0, firstParen); + // Strip off any remaining parentheses due to the CCG parser bug. + const content = nodeString.slice(firstParen + 1, lastParen).replaceAll('(', '').replaceAll(')', ''); + + return { rule, content }; +} + +@Component({ + selector: 'la-parse-tree-table', + imports: [TreeNodeComponent], + templateUrl: './parse-tree-table.component.html', + styleUrl: './parse-tree-table.component.scss' +}) +export class ParseTreeTableComponent { + public readonly tree = input.required(); + + public displayTree = computed(() => buildDisplayTree(this.tree())); } diff --git a/frontend/src/app/annotate/annotation-parse-results/types.ts b/frontend/src/app/annotate/annotation-parse-results/types.ts index 751fc3d5..45c7fce5 100644 --- a/frontend/src/app/annotate/annotation-parse-results/types.ts +++ b/frontend/src/app/annotate/annotation-parse-results/types.ts @@ -1,57 +1,22 @@ -// TODO: update these types to match the actual data structure returned -// by LangPro's syntactic parser. +export type LeafNode = { + // Fixed order: rule, token, lemma, POS tag, NER tag, category. + node: [string, string, string, string, string, string]; +}; -export interface ParseResult { - parser: string; - sentences: ParsedSentence[]; -} +export type UnaryNode = { + node: string; + children: [CCGNode]; +}; -export interface ParsedSentence { - id: string; - text: string; - parses: ParseTree[]; -} +export type BinaryNode = { + node: string; + children: [CCGNode, CCGNode]; +}; -export interface ParseTree { - type: ParseTreeType; - root: ParseTreeNode; -} +export type CCGNode = LeafNode | UnaryNode | BinaryNode; -export enum ParseTreeType { - CCG_DERIVATION, - CCG_TERM, - CORRECTED_CCG_TERM, - FIRST_LLF -} -export type ParseTreeNode = LeafNode | UnaryNode | BinaryNode | VariableNode; - -export interface LeafNode { - type: 'leaf'; - lem: string; - tok: string; - pos: string; - ner: string; - cat: string; -} - -export interface UnaryNode { - type: 'unary'; - child: ParseTreeNode; - cat: string; - rule?: string; -} - -export interface BinaryNode { - type: 'binary'; - left: ParseTreeNode; - right: ParseTreeNode; - cat: string; // e.g. "NP", "VP" - rule?: string; // e.g., "fa", "ba" -} - -export interface VariableNode { - type: 'var'; - name: string; - typeInfo: string; -} +export type ParseResponseData = { + ccg_trees: CCGNode[]; + proofs: unknown[]; +}; diff --git a/frontend/src/app/services/parse.service.ts b/frontend/src/app/services/parse.service.ts index 7b92bf49..ac64795c 100644 --- a/frontend/src/app/services/parse.service.ts +++ b/frontend/src/app/services/parse.service.ts @@ -2,8 +2,13 @@ import { HttpClient } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; import { Subject, switchMap, catchError, of } from 'rxjs'; import { ParseInput } from '@/annotate/annotation-input/annotation-input.component'; +import { ParseResponseData } from '@/annotate/annotation-parse-results/types'; -export type ParseResponse = any; + +export type ParseResponse = { + data: ParseResponseData; + error: string | null; +}; @Injectable({ providedIn: 'root' @@ -18,8 +23,8 @@ export class ParseService { this.http.post("/api/problem/parse", form).pipe( catchError((error) => { console.error(`Error parsing problem:`, error); - return of(null); - }) + return of({ data: null, error: error.message || "An error occurred while parsing the problem." }); + }), ) ) ); From 7337796700344ced2bb76e10ee095f7975e54356 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Mon, 16 Mar 2026 11:51:46 +0100 Subject: [PATCH 03/11] Add scaffolding for future parses --- .../annotation-parse-results.component.html | 12 ++++++--- .../annotation-parse-results.component.ts | 27 ++++++++++++++++++- .../parse-tree-table.component.html | 4 +-- .../parse-tree-table.component.scss | 3 +++ .../parse-tree-table.component.ts | 22 ++++++++------- .../annotation-parse-results/types.ts | 8 +++++- 6 files changed, 58 insertions(+), 18 deletions(-) diff --git a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html index df82c85c..683d3e22 100644 --- a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html +++ b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html @@ -1,17 +1,21 @@ -@let ccgTrees = (parseResults$ | async)?.ccg_trees; +@let ccgParses = (parseResults$ | async);
- @for (tree of ccgTrees; track $index) { + @for (parse of ccgParses; track $index) {

- +
+ @for (tree of parse.ccgTrees; track $index) { + + } +
diff --git a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts index dfe42421..aa0c0070 100644 --- a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts +++ b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts @@ -5,6 +5,31 @@ import { ParseTreeTableComponent } from "./parse-tree-table/parse-tree-table.com import { NgbAccordionModule } from "@ng-bootstrap/ng-bootstrap"; import { map } from "rxjs"; import { CommonModule } from "@angular/common"; +import { CCGNode, CCGParse } from "./types"; + +export interface TreeWithType { + type: string; + tree: CCGNode; +} + +interface FlattenedParseResult { + sentence: string; + ccgTrees: TreeWithType[]; +} + +function flattenResult(parse: CCGParse): FlattenedParseResult { + const { ccg_tree, ccg_term, corr_term, llf } = parse.ccg_trees; + // TODO: Reintroduce the other trees once they are serialized properly. + return { + ...parse, + ccgTrees: [ + { type: "CCG Tree", tree: ccg_tree }, + // { type: "CCG Term", tree: ccg_term }, + // { type: "Corrected CCG Term", tree: corr_term }, + // { type: "Lambda Logical Form", tree: llf } + ] + }; +} @Component({ @@ -20,7 +45,7 @@ export class AnnotationParseResultsComponent { public parseResults$ = this.parseService.parse$ .pipe( - map(response => response?.data || null), + map(response => response?.data?.ccg_parses.map(parse => flattenResult(parse)) ?? null), takeUntilDestroyed(this.destroyRef) ); } diff --git a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.html b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.html index 215c3b28..cdac489a 100644 --- a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.html +++ b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.html @@ -1,6 +1,6 @@ -
+
-

--tree type--

+

{{ treeType() }}

diff --git a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.scss b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.scss index e69de29b..1bdf1e31 100644 --- a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.scss +++ b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.scss @@ -0,0 +1,3 @@ +:host { + width: fit-content; +} diff --git a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts index 0eb666ea..2acd3737 100644 --- a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts +++ b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts @@ -2,6 +2,7 @@ import { Component, computed, input } from '@angular/core'; import { TreeNodeComponent } from './tree-node.component'; import { CCGNode, LeafNode, BinaryNode, UnaryNode } from "../types"; import { TreeNodeDisplay } from "./tree-node.component"; +import { TreeWithType } from '../annotation-parse-results.component'; export function buildDisplayTree(node: CCGNode): TreeNodeDisplay { if (nodeIsLeaf(node)) { @@ -20,7 +21,7 @@ export function buildDisplayTree(node: CCGNode): TreeNodeDisplay { } function nodeIsLeaf(node: CCGNode): node is LeafNode { - return Array.isArray(node.node); + return !('children' in node); } function nodeIsBinary(node: CCGNode): node is BinaryNode { @@ -70,16 +71,16 @@ function buildUnaryNode(node: UnaryNode): TreeNodeDisplay { /** * Parses a node string to extract the rule and the content. - * - * A node string is usually of the form "A(B)", where a is the rule applied + * + * A node string is usually of the form "A(B)", where a is the rule applied * and B is the resulting category. The rule is anything everything before - * the first parenthesis. Everything within it is the content. For example, + * the first parenthesis. Everything within it is the content. For example, * in "fa(s:ng-np)", "fa" is the rule and "s:ng-np" is the content. - * - * Due to a bug in the CCG parser, sometimes the node string can have - * multiple layers of parentheses, e.g. fa(((s:ng-np)-(s:ng-np))). + * + * Due to a bug in the CCG parser, sometimes the node string can have + * multiple layers of parentheses, e.g. fa(((s:ng-np)-(s:ng-np))). * function only strips off the first. - * + * */ function extractRule(nodeString: string): { rule: string, content: string; } { const firstParen = nodeString.indexOf('('); @@ -107,7 +108,8 @@ function extractRule(nodeString: string): { rule: string, content: string; } { styleUrl: './parse-tree-table.component.scss' }) export class ParseTreeTableComponent { - public readonly tree = input.required(); + public readonly tree = input.required(); - public displayTree = computed(() => buildDisplayTree(this.tree())); + public displayTree = computed(() => buildDisplayTree(this.tree().tree)); + public treeType = computed(() => this.tree().type); } diff --git a/frontend/src/app/annotate/annotation-parse-results/types.ts b/frontend/src/app/annotate/annotation-parse-results/types.ts index 45c7fce5..3ac5732f 100644 --- a/frontend/src/app/annotate/annotation-parse-results/types.ts +++ b/frontend/src/app/annotate/annotation-parse-results/types.ts @@ -15,8 +15,14 @@ export type BinaryNode = { export type CCGNode = LeafNode | UnaryNode | BinaryNode; +export type ParseTreeType = 'ccg_tree' | 'ccg_term' | 'corr_term' | 'llf'; + +export interface CCGParse { + sentence: string; + ccg_trees: Record; +}; export type ParseResponseData = { - ccg_trees: CCGNode[]; + ccg_parses: CCGParse[]; proofs: unknown[]; }; From e70833b7c23dbd7630fa64f009c07b92ad5f1257 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Mon, 16 Mar 2026 12:10:55 +0100 Subject: [PATCH 04/11] Fix frontend tests --- .../parse-tree-table.component.spec.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.spec.ts b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.spec.ts index 52537594..d91fe68a 100644 --- a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.spec.ts +++ b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.spec.ts @@ -1,17 +1,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ParseTreeTableComponent } from './parse-tree-table.component'; -import { ParseTree, ParseTreeType } from '../types'; +import { TreeWithType } from '../annotation-parse-results.component'; -const mockTree: ParseTree = { - type: ParseTreeType.CCG_DERIVATION, - root: { - type: "leaf", - lem: "dog", - tok: "Dog", - pos: "NN", - ner: "O", - cat: "N" +const mockTree: TreeWithType = { + type: "CCG Tree", + tree: { + node: ["NP", "The", "the", "DT", "O", "NP"], } } From 2488e92c1c7042d4d1e2ebb93a162ac900a5ba40 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Tue, 17 Mar 2026 09:23:37 +0100 Subject: [PATCH 05/11] Rename syntactic parse tab header --- .../annotation-menu.component.html | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/frontend/src/app/annotate/annotation-menu/annotation-menu.component.html b/frontend/src/app/annotate/annotation-menu/annotation-menu.component.html index 6e926455..dbea7eb0 100644 --- a/frontend/src/app/annotate/annotation-menu/annotation-menu.component.html +++ b/frontend/src/app/annotate/annotation-menu/annotation-menu.component.html @@ -1,35 +1,35 @@ - - -
+ + +
From e650a271ec0275f9ddb913fc01df84c32c297190 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Tue, 17 Mar 2026 09:24:34 +0100 Subject: [PATCH 06/11] Add empty text --- .../annotation-parse-results.component.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html index 683d3e22..7da1ebb9 100644 --- a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html +++ b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html @@ -20,6 +20,10 @@

+ } @empty { +
+ No parse results to display yet. +
}
From 79164573286ede35b32807044aaeb2240e047046 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Tue, 17 Mar 2026 09:30:44 +0100 Subject: [PATCH 07/11] Clear parse results upon navigating to a new problem --- frontend/src/app/services/parse.service.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/services/parse.service.ts b/frontend/src/app/services/parse.service.ts index ac64795c..d9ecde36 100644 --- a/frontend/src/app/services/parse.service.ts +++ b/frontend/src/app/services/parse.service.ts @@ -1,8 +1,9 @@ import { HttpClient } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; -import { Subject, switchMap, catchError, of } from 'rxjs'; +import { Subject, switchMap, catchError, of, merge, map } from 'rxjs'; import { ParseInput } from '@/annotate/annotation-input/annotation-input.component'; import { ParseResponseData } from '@/annotate/annotation-parse-results/types'; +import { ProblemService } from './problem.service'; export type ParseResponse = { @@ -15,10 +16,14 @@ export type ParseResponse = { }) export class ParseService { private http = inject(HttpClient); + private problemService = inject(ProblemService); public submit$ = new Subject(); - public parse$ = this.submit$.pipe( + // Clear parse results when a new problem is loaded. + private clearOnNewProblem$ = this.problemService.problemResponse$.pipe(map(() => null)); + + private parseResults$ = this.submit$.pipe( switchMap((form) => this.http.post("/api/problem/parse", form).pipe( catchError((error) => { @@ -28,4 +33,9 @@ export class ParseService { ) ) ); + + public parse$ = merge( + this.parseResults$, + this.clearOnNewProblem$ + ); } From 97d626b85e53346e38a90383f1b47a4b89c0018f Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Thu, 19 Mar 2026 09:14:33 +0100 Subject: [PATCH 08/11] Moved syntactic parse tree types to central types.ts --- .../annotation-parse-results.component.ts | 2 +- .../parse-tree-table.component.ts | 2 +- .../annotation-parse-results/types.ts | 28 ---------------- frontend/src/app/services/parse.service.ts | 2 +- frontend/src/app/types.ts | 33 ++++++++++++++++++- 5 files changed, 35 insertions(+), 32 deletions(-) delete mode 100644 frontend/src/app/annotate/annotation-parse-results/types.ts diff --git a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts index aa0c0070..fe42a0cf 100644 --- a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts +++ b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts @@ -5,7 +5,7 @@ import { ParseTreeTableComponent } from "./parse-tree-table/parse-tree-table.com import { NgbAccordionModule } from "@ng-bootstrap/ng-bootstrap"; import { map } from "rxjs"; import { CommonModule } from "@angular/common"; -import { CCGNode, CCGParse } from "./types"; +import { CCGNode, CCGParse } from "@/types"; export interface TreeWithType { type: string; diff --git a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts index 2acd3737..2b647d82 100644 --- a/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts +++ b/frontend/src/app/annotate/annotation-parse-results/parse-tree-table/parse-tree-table.component.ts @@ -1,6 +1,6 @@ import { Component, computed, input } from '@angular/core'; import { TreeNodeComponent } from './tree-node.component'; -import { CCGNode, LeafNode, BinaryNode, UnaryNode } from "../types"; +import { CCGNode, LeafNode, BinaryNode, UnaryNode } from "@/types"; import { TreeNodeDisplay } from "./tree-node.component"; import { TreeWithType } from '../annotation-parse-results.component'; diff --git a/frontend/src/app/annotate/annotation-parse-results/types.ts b/frontend/src/app/annotate/annotation-parse-results/types.ts deleted file mode 100644 index 3ac5732f..00000000 --- a/frontend/src/app/annotate/annotation-parse-results/types.ts +++ /dev/null @@ -1,28 +0,0 @@ -export type LeafNode = { - // Fixed order: rule, token, lemma, POS tag, NER tag, category. - node: [string, string, string, string, string, string]; -}; - -export type UnaryNode = { - node: string; - children: [CCGNode]; -}; - -export type BinaryNode = { - node: string; - children: [CCGNode, CCGNode]; -}; - -export type CCGNode = LeafNode | UnaryNode | BinaryNode; - -export type ParseTreeType = 'ccg_tree' | 'ccg_term' | 'corr_term' | 'llf'; - -export interface CCGParse { - sentence: string; - ccg_trees: Record; -}; - -export type ParseResponseData = { - ccg_parses: CCGParse[]; - proofs: unknown[]; -}; diff --git a/frontend/src/app/services/parse.service.ts b/frontend/src/app/services/parse.service.ts index d9ecde36..bbf3a5da 100644 --- a/frontend/src/app/services/parse.service.ts +++ b/frontend/src/app/services/parse.service.ts @@ -2,8 +2,8 @@ import { HttpClient } from '@angular/common/http'; import { inject, Injectable } from '@angular/core'; import { Subject, switchMap, catchError, of, merge, map } from 'rxjs'; import { ParseInput } from '@/annotate/annotation-input/annotation-input.component'; -import { ParseResponseData } from '@/annotate/annotation-parse-results/types'; import { ProblemService } from './problem.service'; +import { ParseResponseData } from '@/types'; export type ParseResponse = { diff --git a/frontend/src/app/types.ts b/frontend/src/app/types.ts index 5e817d7c..9ffd6c00 100644 --- a/frontend/src/app/types.ts +++ b/frontend/src/app/types.ts @@ -132,5 +132,36 @@ export interface Dimensions { height: number; } +// +// Syntactic parse tree types +// + +export type LeafNode = { + // Fixed order: rule, token, lemma, POS tag, NER tag, category. + node: [string, string, string, string, string, string]; +}; + +export type UnaryNode = { + node: string; + children: [CCGNode]; +}; + +export type BinaryNode = { + node: string; + children: [CCGNode, CCGNode]; +}; + +export type CCGNode = LeafNode | UnaryNode | BinaryNode; + +export type ParseTreeType = 'ccg_tree' | 'ccg_term' | 'corr_term' | 'llf'; + +export interface CCGParse { + sentence: string; + ccg_trees: Record; +}; + +export type ParseResponseData = { + ccg_parses: CCGParse[]; + proofs: unknown[]; +}; -export type CCGTerm = string[]; From f1efb9127c898d3c91efc1d86e929403b92df57d Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Thu, 19 Mar 2026 09:29:23 +0100 Subject: [PATCH 09/11] Rename flatten to unfold --- .../annotation-parse-results.component.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts index fe42a0cf..8f34ff67 100644 --- a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts +++ b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.ts @@ -12,12 +12,12 @@ export interface TreeWithType { tree: CCGNode; } -interface FlattenedParseResult { +interface UnfoldedParseResult { sentence: string; ccgTrees: TreeWithType[]; } -function flattenResult(parse: CCGParse): FlattenedParseResult { +function unfoldParseResult(parse: CCGParse): UnfoldedParseResult { const { ccg_tree, ccg_term, corr_term, llf } = parse.ccg_trees; // TODO: Reintroduce the other trees once they are serialized properly. return { @@ -45,7 +45,7 @@ export class AnnotationParseResultsComponent { public parseResults$ = this.parseService.parse$ .pipe( - map(response => response?.data?.ccg_parses.map(parse => flattenResult(parse)) ?? null), + map(response => response?.data?.ccg_parses.map(parse => unfoldParseResult(parse)) ?? null), takeUntilDestroyed(this.destroyRef) ); } From a4292ddc5f2b6e807896f27fd58c44d7f7e0709b Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Thu, 19 Mar 2026 09:29:45 +0100 Subject: [PATCH 10/11] Expand help text in case of no parse results --- .../annotation-parse-results.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html index 7da1ebb9..27001271 100644 --- a/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html +++ b/frontend/src/app/annotate/annotation-parse-results/annotation-parse-results.component.html @@ -22,7 +22,7 @@

} @empty {
- No parse results to display yet. + No parse results to display yet. Press the 'Parse and Prove' button to see the parse results.
}
From 178f5bb94995d647fac2799485cdf7766d7484e5 Mon Sep 17 00:00:00 2001 From: Xander Vertegaal Date: Thu, 19 Mar 2026 09:43:18 +0100 Subject: [PATCH 11/11] Replace obsolete CCGTerm type --- frontend/src/app/annotate/parse-tree/parse-svg.component.ts | 4 ++-- frontend/src/app/annotate/parse-tree/parse-term.component.ts | 4 ++-- frontend/src/app/annotate/parse-tree/parse-tree.component.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/annotate/parse-tree/parse-svg.component.ts b/frontend/src/app/annotate/parse-tree/parse-svg.component.ts index 48e25295..16bcddaa 100644 --- a/frontend/src/app/annotate/parse-tree/parse-svg.component.ts +++ b/frontend/src/app/annotate/parse-tree/parse-svg.component.ts @@ -1,7 +1,7 @@ import { Component, ChangeDetectorRef, ElementRef, Input, ViewChild, afterNextRender} from '@angular/core'; import { CommonModule } from "@angular/common"; import { Subject } from "rxjs"; -import { CCGTerm, Dimensions } from '@/types'; +import { Dimensions } from '@/types'; import { ParseTree } from './parse-tree.component'; import { Tree } from "@/tree"; import svgPanZoom from 'svg-pan-zoom'; @@ -35,5 +35,5 @@ export class ParseSVG { } @Input() - tree: Tree = Tree.empty(); + tree: Tree = Tree.empty(); } diff --git a/frontend/src/app/annotate/parse-tree/parse-term.component.ts b/frontend/src/app/annotate/parse-tree/parse-term.component.ts index 772abb3a..dfcd778c 100644 --- a/frontend/src/app/annotate/parse-tree/parse-term.component.ts +++ b/frontend/src/app/annotate/parse-tree/parse-term.component.ts @@ -1,5 +1,5 @@ import { Component, ElementRef, Input, Output, ViewChild, EventEmitter } from '@angular/core'; -import { CCGTerm, Dimensions } from '@/types'; +import { Dimensions } from '@/types'; @Component({ selector: "[parse-term]", @@ -19,7 +19,7 @@ export class ParseTerm { public bg?: string; @Input() - public term: CCGTerm = []; + public term: string[] = []; @Input() public end: boolean = false; diff --git a/frontend/src/app/annotate/parse-tree/parse-tree.component.ts b/frontend/src/app/annotate/parse-tree/parse-tree.component.ts index 9b22fad8..eef27499 100644 --- a/frontend/src/app/annotate/parse-tree/parse-tree.component.ts +++ b/frontend/src/app/annotate/parse-tree/parse-tree.component.ts @@ -1,6 +1,6 @@ import { Component, Input, Output, EventEmitter } from '@angular/core'; import { ParseTerm } from './parse-term.component'; -import { Dimensions, CCGTerm } from '@/types'; +import { Dimensions } from '@/types'; import { TreeNode } from "@/tree"; @Component({ @@ -14,7 +14,7 @@ export class ParseTree { expanded: boolean = true; @Input() - treeNode: TreeNode = {value: [], children: []}; + treeNode: TreeNode = {value: [], children: []}; levelHeight = 40;