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

Refactor of reconstruct.ts #484

Draft
wants to merge 111 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
a08e74a
wip, doc: Comments for questions in reconstruct.ts
Ehcsan Nov 7, 2023
e367620
wip, refactor: Comments to group parts of reconstruct.ts
Ehcsan Nov 7, 2023
de9289d
Merge branch 'main' into main
Ehcsan Nov 7, 2023
19041f3
wip, refactor: refactor of 'reconstructToCode' into main.ts and seper…
Ehcsan Nov 13, 2023
e5d9a30
typo: corrected typo of reconstruct in reconstruct.ts
Ehcsan Nov 13, 2023
5c90575
wip, refactor: refactor of 'helper functions', as declared in earlier…
Ehcsan Nov 13, 2023
f546c5f
Merge branch 'Code-Inspect:main' into main
Ehcsan Nov 22, 2023
9971485
Merge branch 'Code-Inspect:main' into main
Ehcsan Nov 23, 2023
bda524a
wip, feat: Added parts to PrettyPrintLine
Ehcsan Nov 24, 2023
3006674
Merge branch 'Code-Inspect:main' into main
EagleoutIce Nov 24, 2023
86a2cd1
Merge branch 'main' of https://github.com/Ehcsan/flowr
Ehcsan Nov 24, 2023
5dce5a0
Merge branch 'Code-Inspect:main' into main
Ehcsan Nov 30, 2023
7c64c31
Merge branch 'main' of https://github.com/Ehcsan/flowr
Ehcsan Nov 30, 2023
5995277
feat(reconstruct): lineParts now work with locations
Ehcsan Nov 30, 2023
030edc9
fix(reconstruct): fixed problems the linter gave
Ehcsan Nov 30, 2023
03cbf9e
wip(reconstruct): cleaned up based on suggestions
Ehcsan Dec 1, 2023
329a0af
fix(reconstruct): fixed linter recommendations in helper.ts
Ehcsan Dec 1, 2023
428d7bf
feat: code update during the meeting
EagleoutIce Dec 1, 2023
f33ad56
Merge branch 'Code-Inspect:main' into main
Ehcsan Dec 7, 2023
82552b7
fix(reconstruct): pretty-print-to-string use the result :)
EagleoutIce Dec 8, 2023
f1adaa5
refactor(doc): fix typo (theis->this)
EagleoutIce Dec 8, 2023
36a1df4
feat(delims): limit subtype
EagleoutIce Dec 8, 2023
0446887
refactor(delimiter): improve RDelimiter node type
EagleoutIce Dec 8, 2023
bc156d9
Merge branch 'Code-Inspect:main' into main
Ehcsan Dec 13, 2023
508d152
Merge branch 'Code-Inspect:main' into main
Ehcsan Dec 13, 2023
998a6d3
feat(test): added first tests for plain and merge of reconstruct
Ehcsan Dec 13, 2023
2e2472e
feat-fix(reconstruct): changes that got lost
Ehcsan Dec 13, 2023
0cd9ab2
feat(test): added more cases to merge
Ehcsan Dec 13, 2023
7b9cb5b
refactor(reconstruct, test): minor meeting improvements
EagleoutIce Dec 15, 2023
b70c91d
Merge branch 'main' into main
EagleoutIce Dec 21, 2023
698febf
feat-fix(reconstruct): fixed error with columns
Ehcsan Dec 22, 2023
b9cc3dd
Merge branch 'main' into main
EagleoutIce Dec 22, 2023
624acf2
refactor(merge): fix dirty merge
EagleoutIce Dec 22, 2023
7c0c3c3
feat(reconstruct): work on more sensible reconstructions in for, repe…
EagleoutIce Dec 22, 2023
2897b9b
test-fix(test): fixed for-loop reconstruction
Ehcsan Jan 19, 2024
19ae981
wip: first stage of adding `(`, `in`, and `)` to the output
EagleoutIce Jan 19, 2024
089eb6a
test-fix(reconstruct): while Loop Reconstruction
Ehcsan Jan 24, 2024
2701d40
Merge branch 'main' of https://github.com/Ehcsan/flowr
Ehcsan Jan 24, 2024
a172d2b
lint: linter fixes
Ehcsan Jan 24, 2024
ef2d55b
feat-fix: fixed linting recomendations
Ehcsan Jan 25, 2024
6c7c02e
Merge branch 'Code-Inspect:main' into main
Ehcsan Jan 26, 2024
037d4ef
wip(reconstruct): attemt to implement (, in, )
Ehcsan Jan 26, 2024
d482745
add a handler for `ForIn` as part of normalize delimiter
EagleoutIce Jan 26, 2024
d2d3d87
feat(wip): might have broken reconstruction!!!
Ehcsan Jan 28, 2024
858a726
feat-fix(reconstruct): for-loop finished
Ehcsan Jan 30, 2024
744e160
feat-fix(reconstruct): while-loop exept empty body
Ehcsan Jan 30, 2024
08141bd
feat-fix(reconstruct): fixed while-loop
Ehcsan Jan 31, 2024
22911ad
feat-fix(reconstruct): cleaned up comments
Ehcsan Jan 31, 2024
1030310
test-fix(reconstruct): fixed nested assignments
Ehcsan Jan 31, 2024
47a7d93
feat-fix(reconstruct): started fixing function def
Ehcsan Feb 9, 2024
659574c
test(reconstruct): testing function definition
Ehcsan Feb 9, 2024
2bdd813
test-fix(reconstruct): debugging for function def
Ehcsan Feb 9, 2024
010db50
test(slicing): mermaid link shown again
Ehcsan Feb 23, 2024
5345f19
minor patches to get the tests running
EagleoutIce Feb 23, 2024
20ec3a8
removing only marker
EagleoutIce Feb 23, 2024
6ffcf9e
tests-fix(reconstruct): adjusted linePart tests
Ehcsan Feb 23, 2024
a8229ba
test-fix(reconstruct): removed .only from test
Ehcsan Feb 23, 2024
e245679
Merge branch 'main' of https://github.com/Ehcsan/flowr
Ehcsan Feb 23, 2024
5425223
test(reconstruct): removed unneccessary comment
Ehcsan Feb 23, 2024
227348b
feat-fix(reconstruct): removed unused import
Ehcsan Feb 23, 2024
23b0ab0
Merge branch 'main' into 484-refactor-reconstruct
EagleoutIce Feb 23, 2024
5ae5b74
lint-fix, refactor(reconstruct): some love?
EagleoutIce Feb 23, 2024
df6e39f
refactor: remove console logging and stuff
EagleoutIce Feb 23, 2024
5c68caf
Merge remote-tracking branch 'upstream/main'
Ehcsan Feb 27, 2024
2ef8892
feat-fix: fixed linting problems
Ehcsan Feb 27, 2024
677d8b8
test-fix(reconstructed): function definition reconstruct
Ehcsan Feb 27, 2024
f2ae684
wip: maintaining function body after reconstruct
EagleoutIce Mar 1, 2024
fd8e413
wip: handle parameters in fun-def reconstruct
EagleoutIce Mar 1, 2024
6d59c2e
feat: reconstruct calls correctly
EagleoutIce Mar 1, 2024
0ff9142
Merge branch 'Code-Inspect:main' into main
Ehcsan Mar 7, 2024
c9f01b1
tests-fix(reconstruct): fixed layout-preserving
Ehcsan Mar 7, 2024
9c4f04d
Merge branch 'main' of https://github.com/Ehcsan/flowr
Ehcsan Mar 7, 2024
97ce67d
further reconstruct
EagleoutIce Mar 8, 2024
73e3d46
test(reconstruct): started work on if testing
Ehcsan Mar 8, 2024
94d0e4b
feat(reconstruct): lint fixes
Ehcsan Mar 8, 2024
4c4abcf
feat(reconstruct): changed if-reconstruct; WIP
Ehcsan Mar 15, 2024
4795997
Merge branch 'main' into 484-refactor-reconstruct
EagleoutIce Mar 15, 2024
08fcb2e
Merge branch 'main' into 484-refactor-reconstruct
EagleoutIce Mar 15, 2024
fe57bd2
fix: imp for result reconstruction
EagleoutIce Mar 15, 2024
600abc9
doc: remove question comment
EagleoutIce Mar 15, 2024
870a9f2
feat(reconstruct): if-body works, else-body doesnt
Ehcsan Mar 15, 2024
576d17e
feat(reconstruct): if reconstruct good but broken
Ehcsan Mar 19, 2024
9f1bcc9
Merge branch 'main' into 484-refactor-reconstruct
EagleoutIce Mar 22, 2024
8a403e0
working on tests
EagleoutIce Mar 22, 2024
812b291
test-fix(reconstruct): tried finding correct index
Ehcsan Apr 5, 2024
0231dd6
if fixes
EagleoutIce Apr 5, 2024
bbfa26d
tests-fix(reconstruction): added layout preserving
Ehcsan Apr 25, 2024
3c0e2c2
tests-fix(reconstruct): more test fixes
Ehcsan Apr 25, 2024
7bf2750
tests(reconstruct): made changes to isolate failed tests
Ehcsan Apr 25, 2024
63be95e
test(reconstruct): added test to better understand assignments
Ehcsan Apr 25, 2024
0e5bf7c
fix: local patches
EagleoutIce Apr 26, 2024
d8004b6
test-fix(reconstruct): some fixes and minor change to merge
Ehcsan Apr 29, 2024
eef7854
lint-fix(reconstruct): linter fixes
Ehcsan Apr 29, 2024
2ca0554
feat-fix(reconstruct): implemented merge in function call
Ehcsan Apr 29, 2024
183dac0
feat(reconstruct): started work on semicolon adding
Ehcsan May 3, 2024
71a79c7
feat(reconstruction): semicolon problem solving
Ehcsan May 6, 2024
04f9b02
feat(reconstruct): finished semicolon reconstruction
Ehcsan May 7, 2024
2ea0759
Merge remote-tracking branch 'upstream/main' into 484-refactor-recons…
Ehcsan May 23, 2024
b26c780
feat(reconstruct): started cleaning up merge
Ehcsan May 23, 2024
7f5afec
feat-fix: handle some of the problems introduced by the merge
EagleoutIce May 24, 2024
ec43a73
feat(reconstruct): somehow there are no errors but its broken
Ehcsan May 30, 2024
57349a6
lint-fix(reconstruct): now the linter doesnt scream
Ehcsan May 30, 2024
c4cac54
feat-fix(reconstruct): fixed access to undefined
Ehcsan May 31, 2024
3132580
feat(reconstruct): some minor quality improvements
Ehcsan Jun 13, 2024
36565d0
tests-fix(reconstruct): started fixing test
Ehcsan Jun 21, 2024
d25e464
feat(reconstruct): changed reconstruction to work with new {}
Ehcsan Jun 27, 2024
eb5b11e
test(reconstruct): test for plainSplit
Ehcsan Jul 11, 2024
66888f5
feat-fix(reconstruct): fixed grouping
Ehcsan Jul 11, 2024
9261280
feat-fix(reconstruct): fixed indent and for
Ehcsan Jul 18, 2024
5b3ede2
tests-fix(reconstruct): dataflow v2 changes
Ehcsan Aug 9, 2024
d036b65
tests-fix: started fising calls-tests.ts
Ehcsan Aug 13, 2024
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
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"cSpell.words": [
"unnesseccary"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Das sollte nicht gemutet werden :D

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ich wusste gar nicht das in den settings was geändert wurde 😅

]
}
4 changes: 2 additions & 2 deletions src/core/steps/all/static-slicing/10-reconstruct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import type { IPipelineStep } from '../../pipeline-step'
import { PipelineStepStage } from '../../pipeline-step'
import type { DeepReadonly } from 'ts-essentials'
import type { SliceResult } from '../../../../slicing/static/slicer-types'
import type { AutoSelectPredicate } from '../../../../reconstruct/reconstruct'
import { reconstructToCode } from '../../../../reconstruct/reconstruct'
import type { NormalizedAst } from '../../../../r-bridge/lang-4.x/ast/model/processing/decorate'
import { reconstructToCode } from '../../../../reconstruct/main'
import type { AutoSelectPredicate } from '../../../../reconstruct/helper'

export interface ReconstructRequiredInput {
autoSelectIf?: AutoSelectPredicate
Expand Down
1 change: 0 additions & 1 deletion src/r-bridge/lang-4.x/ast/model/nodes/info/r-delimiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { RawRType, RType } from '../../type'
import type { Location } from '../../model'
import type { MergeableRecord } from '../../../../../../util/objects'


/**
* Combines '`{`', '`}`', '`(`', '`)`', and other delimiters used by R, they are ignored for most analysis
* but helpful during reconstruction.
Expand Down
284 changes: 284 additions & 0 deletions src/reconstruct/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
import type { NodeId } from '../r-bridge/lang-4.x/ast/model/processing/node-id'
import type { ParentInformation } from '../r-bridge/lang-4.x/ast/model/processing/decorate'
import type { RNode } from '../r-bridge/lang-4.x/ast/model/model'
import { RType } from '../r-bridge/lang-4.x/ast/model/type'
import type { SourcePosition } from '../util/range'
import type { ReconstructionConfiguration } from './reconstruct'

export type Code = PrettyPrintLine[]
export type Selection = ReadonlySet<NodeId>
export interface PrettyPrintLinePart {
part: string
loc: SourcePosition
}
export interface PrettyPrintLine {
linePart: PrettyPrintLinePart[]
EagleoutIce marked this conversation as resolved.
Show resolved Hide resolved
indent: number
}

/**
* Splits text on linebreak to create lineParts and encapsulates them in the Code type
*/
export function plain(text: string, location: SourcePosition): Code {
const printLine: PrettyPrintLine = { linePart: [], indent: 0 }
const split = text.split('\n')
let locationLine = location[0]

for(const line of split) {
printLine.linePart.push({ part: line, loc: [locationLine++, location[1]] })
}
return [printLine]
}
export function plainSplit(text: string, location: SourcePosition): Code {
const printLine: PrettyPrintLine = { linePart: [], indent: 0 }
let i = 0
let token = ''
let currLoc: SourcePosition = [location[0], location[1]]
while(i < text.length) {
if(text[i] === ' ') {
if(!(token === '')) {
printLine.linePart.push({ part: token, loc: currLoc })
}
currLoc = [currLoc[0], currLoc[1] + token.length + 1]
token = ''
} else if(text[i] === '\n') {
printLine.linePart.push({ part: token, loc: currLoc })
currLoc = [currLoc[0] + 1, location[1]]
token = ''
} else {
token = token.concat(text[i])
}
i++
}
printLine.linePart.push({ part: token, loc: currLoc })
return [printLine]
}

/**
* this function will merge up to n code pieces into a singular code piece, garanting that there are no duplicate lines and all lines are in order
*/
export function merge(...snipbits: Code[]): Code {
const buckets: PrettyPrintLine[] = []
const result: Code = []

//separate and group lineParts by lines
for(const code of snipbits) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

die ganze Schleife können wir glaube ich auslagern als ein groupSnippetsByLineNumber oder so (oder genereller als ein group by, aber ich glaube das schenkt uns nichts.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Das stimmt. Ich schau mir das mal an

for(const line of code) {
if(line === undefined) {
continue
}
for(const part of line.linePart) {
const lineNumber = part.loc[0]
if(buckets[lineNumber] === undefined) { //may be necessary as empty elements count as undefined and we don't want to reassign filled buckets
buckets[lineNumber] = { linePart: [], indent: line.indent }
}
buckets[lineNumber].linePart.push(part)
}
}
}

//sort buckets by column and stich lines into single code piece
for(const line of buckets) {
if(line === undefined){ //appears to be necessary as 'buckets' may be sparse (empty elements count as undefined)
continue
}
EagleoutIce marked this conversation as resolved.
Show resolved Hide resolved
line.linePart.sort((a, b) => a.loc[1] - b.loc[1])
result.push(line)
}

return result
}

export function prettyPrintPartToString(line: PrettyPrintLinePart[],columnOffset: number): string {
if(line.length === 0) {
return ''
}
if(line.length === 1) {
return /*' '.repeat(Math.max(columnOffset - 1, 0)) + */line[0].part
}
line.sort((a, b) => a.loc[1] - b.loc[1])
let result = ''
for(const part of line) {
const currLength = result.length + columnOffset
//we have to 0 any negative values as they can happen???
result += ' '.repeat(Math.max(part.loc[1] - currLength, 0))
result = result.concat(part.part)
}
return result
}

export function indentBy(lines: Code, indent: number): Code {
return lines.map(({ linePart, indent: i }) => ({ linePart, indent: i + indent }))
}

export function isSelected(configuration: ReconstructionConfiguration, n: RNode<ParentInformation>) {
return configuration.selection.has(n.info.id) || configuration.autoSelectIf(n)
}

export function removeExpressionListWrap(code: Code) {
if(code.length > 0 && code[0].linePart[0].part === '{' && code[code.length - 1].linePart[code[code.length - 1].linePart.length - 1].part === '}') {
return indentBy(code.slice(1, code.length - 1), -1)
} else {
return code
}
}

/** The structure of the predicate that should be used to determine if a given normalized node should be included in the reconstructed code independent of if it is selected by the slice or not */
export type AutoSelectPredicate = (node: RNode<ParentInformation>) => boolean

export function doNotAutoSelect(_node: RNode<ParentInformation>): boolean {
return false
}

export const libraryFunctionCall = /^(library|require|((require|load|attach)Namespace))$/

export function autoSelectLibrary(node: RNode<ParentInformation>): boolean {
if(node.type !== RType.FunctionCall || node.flavor !== 'named') {
return false
}
return libraryFunctionCall.test(node.functionName.content)
}

export function getIndentString(indent: number): string {
return ' '.repeat(indent * 4)
}

/*
function dist(pos1: number, pos2: number) {
Math.abs(pos1 - pos2)
}
*/

function addSemis(code: Code): Code {

function contains(array: string[], elem: string): boolean {
if(elem === '<-' || elem === '->' || elem === '<<-' || elem === '->>') {
return true
}
if(elem === 'in' || elem === ' {} ') {
return true
}
for(const arrElem of array) {
if(elem === arrElem) {
return true
}
}
return false
}

const line: PrettyPrintLinePart[][] = []
const specialChar = ['+', '-', '*', '/', ':', '<-', '->', '<<-', '->>', '$', '$$', '&', '&&', '||', '?', '<', '>', '=', '<=', '>=', '==', '(', ')', '((', '))', '{', '}', '[', '[[', ']', ']]', 'for', ' in ']
//find a way to make this work with merge, as this is a very similar piece of code
for(const elem of code) {
let currLine = 1
for(const linePart of elem.linePart) {
currLine = linePart.loc[0]
if(line[currLine] === undefined) {
line[currLine] = []
}
line[currLine].push(linePart)
}
}

//iterate through all elements of the code piece to search for places for semicolons
for(const lineElements of line) {
if(lineElements === undefined) {
continue
}
//create a heuristic to store information about the current search
const heuristic = { assignment: false, brackets: false, lastChar: lineElements[0], statement: false, addedSemi: false, curlyBrackets: false }
let possibleSemi = heuristic.lastChar.loc
lineElements.splice(0, 1)
for(const elem of lineElements) {

const lastChar = heuristic.lastChar.part
heuristic.brackets = lastChar[lastChar.length - 1] === ')'
heuristic.curlyBrackets = lastChar[lastChar.length - 1] === '}'
heuristic.statement = !contains(specialChar, heuristic.lastChar.part)

if(heuristic.addedSemi) {
heuristic.assignment = false
}

//check if the current element may be followed by a semicolon
if((elem.loc[1] - (heuristic.lastChar.loc[1] + heuristic.lastChar.part.length)) >= 1) {
//closing brackets
possibleSemi = updateSemi(possibleSemi, heuristic)
} else if(elem.part[elem.part.length - 1] === '}') {
//closing curlyBrackets
possibleSemi = updateSemi(possibleSemi, heuristic)
} else if(elem.part[elem.part.length - 1] === ')') {
//large space
possibleSemi = updateSemi(possibleSemi, heuristic)
}

//checking condishions for adding semicolons
if((elem.part === '<-') || (elem.part === '->') || (elem.part === '<<-') || (elem.part === '->>')) {
//check for assignments
if(heuristic.assignment) {
pushSemi(heuristic, possibleSemi)
}
heuristic.assignment = !heuristic.assignment
} else if(elem.part[0] === '(') {
//check for brackets
heuristic.assignment = false
if(heuristic.brackets) {
pushSemi(heuristic, possibleSemi)
heuristic.brackets = false
}
} else if(elem.part[0] === '{') {
//check for curlyBrackets
heuristic.assignment = false
if(heuristic.curlyBrackets) {
pushSemi(heuristic, possibleSemi)
heuristic.curlyBrackets = false
}
} else if(!contains(specialChar, elem.part)) {
//check for two consecutive statements
if(heuristic.statement) {
pushSemi(heuristic, possibleSemi)
}
}

//update the last character seen
heuristic.lastChar = elem
}
}
code = merge(code)
return code

function pushSemi(heuristic: { assignment: boolean; brackets: boolean; lastChar: PrettyPrintLinePart; statement: boolean; addedSemi: boolean; curlyBrackets: boolean }, possibleSemi: SourcePosition) {
if(!heuristic.addedSemi) {
code.push({ linePart: [{ part: ';', loc: possibleSemi }], indent: 0 })
heuristic.addedSemi = true
}
}

function updateSemi(possibleSemi: SourcePosition, heuristic: { assignment: boolean; brackets: boolean; lastChar: PrettyPrintLinePart; statement: boolean; addedSemi: boolean; curlyBrackets: boolean }) {
const lastSemi: SourcePosition = [possibleSemi[0], possibleSemi[1]]
const other: SourcePosition = [heuristic.lastChar.loc[0], heuristic.lastChar.loc[1] + heuristic.lastChar.part.length]
possibleSemi = other
heuristic.addedSemi = (lastSemi[0] === possibleSemi[0]) && (lastSemi[1] === possibleSemi[0])
return possibleSemi
}
}

export function prettyPrintCodeToString(code: Code, lf = '\n'): string {
code = merge(code)
code = addSemis(code)
return code.map(({ linePart, indent }) => `${getIndentString(Math.max(indent, 0))}${prettyPrintPartToString(linePart, code[0].linePart[0].loc[1])}`).join(lf)
}

export function removeOuterExpressionListIfApplicable(result: PrettyPrintLine[]): Code {
const first = result[0]?.linePart
if(result.length === 1 && first[0].part === '{' && first[result[0].linePart.length - 1].part === '}') {
// we are in a single line
return [{ linePart: first.slice(1, first.length - 1), indent: result[0].indent }]
} else if(result.length > 1 && first[0].part === '{' && result[result.length - 1].linePart[result[result.length - 1].linePart.length - 1].part === '}') {
// remove outer block
return indentBy(result.slice(1, result.length - 1), -1)
} else {
return result
}
}

49 changes: 49 additions & 0 deletions src/reconstruct/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { reconstructLogger, reconstructAstFolds } from './reconstruct'
import type { ReconstructionResult } from './reconstruct'
import { prettyPrintCodeToString, removeOuterExpressionListIfApplicable , autoSelectLibrary } from './helper'

import type { AutoSelectPredicate , Selection } from './helper'


import { LogLevel } from '../util/log'
import type { RNode } from '../r-bridge/lang-4.x/ast/model/model'
import type { NormalizedAst, ParentInformation } from '../r-bridge/lang-4.x/ast/model/processing/decorate'
import { foldAstStateful } from '../r-bridge/lang-4.x/ast/model/processing/stateful-fold'



/**
* Reconstructs parts of a normalized R ast into R code on an expression basis.
*
* @param ast - The {@link NormalizedAst|normalized ast} to be used as a basis for reconstruction
* @param selection - The selection of nodes to be reconstructed (probably the {@link NodeId|NodeIds} identified by the slicer)
* @param autoSelectIf - A predicate that can be used to force the reconstruction of a node (for example to reconstruct library call statements, see {@link autoSelectLibrary}, {@link doNotAutoSelect})
*
* @returns The number of times `autoSelectIf` triggered, as well as the reconstructed code itself.
*/

export function reconstructToCode<Info>(ast: NormalizedAst<Info>, selection: Selection, autoSelectIf: AutoSelectPredicate = autoSelectLibrary): ReconstructionResult {
if(reconstructLogger.settings.minLevel >= LogLevel.Trace) {
reconstructLogger.trace(`reconstruct ast with ids: ${JSON.stringify([...selection])}`)
}

// we use a wrapper to count the number of times the autoSelectIf predicate triggered
let autoSelected = 0
const autoSelectIfWrapper = (node: RNode<ParentInformation>) => {
const result = autoSelectIf(node)
if(result) {
autoSelected++
}
return result
}

// fold of the normalized ast
const result = foldAstStateful(ast.ast, { selection, autoSelectIf: autoSelectIfWrapper }, reconstructAstFolds)

//console.log(JSON.stringify(result))
if(reconstructLogger.settings.minLevel >= LogLevel.Trace) {
reconstructLogger.trace('reconstructed ast before string conversion: ', JSON.stringify(result))
}

return { code: prettyPrintCodeToString(removeOuterExpressionListIfApplicable(result)), linesWithAutoSelected: autoSelected }
}
Loading