inital vesion of recast working
This commit is contained in:
@ -123,7 +123,7 @@ function makeExpressionStatement(
|
||||
}
|
||||
}
|
||||
|
||||
interface CallExpression extends GeneralStatement {
|
||||
export interface CallExpression extends GeneralStatement {
|
||||
type: 'CallExpression'
|
||||
callee: Identifier
|
||||
arguments: Value[]
|
||||
@ -244,7 +244,7 @@ function makeVariableDeclaration(
|
||||
}
|
||||
}
|
||||
|
||||
type Value =
|
||||
export type Value =
|
||||
| Literal
|
||||
| Identifier
|
||||
| BinaryExpression
|
||||
@ -371,7 +371,7 @@ export type BinaryPart = Literal | Identifier
|
||||
// | LogicalExpression
|
||||
// | ConditionalExpression
|
||||
|
||||
interface Literal extends GeneralStatement {
|
||||
export interface Literal extends GeneralStatement {
|
||||
type: 'Literal'
|
||||
value: string | number | boolean | null
|
||||
raw: string
|
||||
@ -459,7 +459,7 @@ function makeBinaryExpression(
|
||||
}
|
||||
}
|
||||
|
||||
interface SketchExpression extends GeneralStatement {
|
||||
export interface SketchExpression extends GeneralStatement {
|
||||
type: 'SketchExpression'
|
||||
body: BlockStatement
|
||||
}
|
||||
@ -483,7 +483,7 @@ function makeSketchExpression(
|
||||
}
|
||||
}
|
||||
|
||||
interface FunctionExpression extends GeneralStatement {
|
||||
export interface FunctionExpression extends GeneralStatement {
|
||||
type: 'FunctionExpression'
|
||||
id: Identifier | null
|
||||
params: Identifier[]
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { recast } from './recast'
|
||||
import { Program } from './abstractSyntaxTree'
|
||||
import { abstractSyntaxTree } from './abstractSyntaxTree'
|
||||
import { lexer } from './tokeniser'
|
||||
import { Token } from './tokeniser'
|
||||
import { Program, abstractSyntaxTree } from './abstractSyntaxTree'
|
||||
import { lexer, Token } from './tokeniser'
|
||||
import fs from 'node:fs'
|
||||
|
||||
describe('recast', () => {
|
||||
it('recasts a simple program', () => {
|
||||
@ -26,6 +25,56 @@ describe('recast', () => {
|
||||
const { ast: ast2 } = code2ast(codeWithOtherQuotes)
|
||||
expect(recast(ast2)).toBe(codeWithOtherQuotes)
|
||||
})
|
||||
it('test assigning two variables, the second summing with the first', () => {
|
||||
const code = `const myVar = 5
|
||||
const newVar = myVar + 1`
|
||||
const { ast } = code2ast(code)
|
||||
const recasted = recast(ast)
|
||||
expect(recasted).toBe(code)
|
||||
})
|
||||
it('test assigning a var by cont concatenating two strings string', () => {
|
||||
const code = fs.readFileSync(
|
||||
'./src/lang/testExamples/variableDeclaration.cado',
|
||||
'utf-8'
|
||||
)
|
||||
const { ast } = code2ast(code)
|
||||
const recasted = recast(ast)
|
||||
expect(recasted).toBe(code.trim())
|
||||
})
|
||||
it('test with function call', () => {
|
||||
const code = `
|
||||
const myVar = "hello"
|
||||
log(5, myVar)`
|
||||
const { ast } = code2ast(code)
|
||||
const recasted = recast(ast)
|
||||
expect(recasted).toBe(code.trim())
|
||||
})
|
||||
it('function declaration with call', () => {
|
||||
const code =
|
||||
[
|
||||
'fn funcN = (a, b) => {',
|
||||
' return a + b',
|
||||
'}',
|
||||
'const theVar = 60',
|
||||
'const magicNum = funcN(9, theVar)',
|
||||
].join('\n')
|
||||
const { ast } = code2ast(code)
|
||||
const recasted = recast(ast)
|
||||
expect(recasted).toBe(code.trim())
|
||||
})
|
||||
it('sketch declaration', () => {
|
||||
let code = `sketch mySketch {
|
||||
path myPath = lineTo(0, 1)
|
||||
lineTo(1, 1)
|
||||
path rightPath = lineTo(1, 0)
|
||||
close()
|
||||
}
|
||||
show(mySketch)
|
||||
`
|
||||
const { ast } = code2ast(code)
|
||||
const recasted = recast(ast)
|
||||
expect(recasted).toBe(code.trim())
|
||||
})
|
||||
})
|
||||
|
||||
// helpers
|
||||
|
@ -1,26 +1,58 @@
|
||||
import { Program, BinaryExpression, BinaryPart } from './abstractSyntaxTree'
|
||||
import {
|
||||
Program,
|
||||
BinaryExpression,
|
||||
BinaryPart,
|
||||
Literal,
|
||||
CallExpression,
|
||||
Value,
|
||||
FunctionExpression,
|
||||
SketchExpression,
|
||||
} from './abstractSyntaxTree'
|
||||
|
||||
export function recast(ast: Program, previousWrittenCode = ''): string {
|
||||
export function recast(
|
||||
ast: Program,
|
||||
previousWrittenCode = '',
|
||||
indentation = ''
|
||||
): string {
|
||||
return ast.body
|
||||
.map((statement) => {
|
||||
if (statement.type === 'ExpressionStatement') {
|
||||
if (statement.expression.type === 'BinaryExpression') {
|
||||
return recastBinaryExpression(statement.expression)
|
||||
return indentation + recastBinaryExpression(statement.expression)
|
||||
} else if (statement.expression.type === 'CallExpression') {
|
||||
return indentation + recastCallExpression(statement.expression)
|
||||
}
|
||||
} else if (statement.type === 'VariableDeclaration') {
|
||||
return statement.declarations
|
||||
.map((declaration) => {
|
||||
if (declaration.init.type === 'BinaryExpression') {
|
||||
return `${statement.kind} ${
|
||||
return `${indentation}${statement.kind} ${
|
||||
declaration.id.name
|
||||
} = ${recastBinaryExpression(declaration.init)}`
|
||||
} else if (declaration.init.type === 'Literal') {
|
||||
return `${statement.kind} ${declaration.id.name} = ${declaration.init.value}`
|
||||
return `${indentation}${statement.kind} ${
|
||||
declaration.id.name
|
||||
} = ${recastLiteral(declaration.init)}`
|
||||
} else if (declaration.init.type === 'FunctionExpression') {
|
||||
return `${indentation}${statement.kind} ${
|
||||
declaration.id.name
|
||||
} = ${recastFunction(declaration.init)}`
|
||||
} else if (declaration.init.type === 'CallExpression') {
|
||||
return `${indentation}${statement.kind} ${
|
||||
declaration.id.name
|
||||
} = ${recastCallExpression(declaration.init)}`
|
||||
} else if (declaration.init.type === 'SketchExpression') {
|
||||
return `${indentation}${statement.kind} ${
|
||||
declaration.id.name
|
||||
} ${recastSketchExpression(declaration.init, indentation)}`
|
||||
}
|
||||
return ''
|
||||
})
|
||||
.join('')
|
||||
} else if (statement.type === 'ReturnStatement') {
|
||||
return `${indentation}return ${recastArgument(statement.argument)}`
|
||||
}
|
||||
|
||||
return statement.type
|
||||
})
|
||||
.join('\n')
|
||||
@ -34,13 +66,53 @@ function recastBinaryExpression(expression: BinaryExpression): string {
|
||||
|
||||
function recastBinaryPart(part: BinaryPart): string {
|
||||
if (part.type === 'Literal') {
|
||||
if (typeof part.value === 'string') {
|
||||
const quote = part.raw.includes('"') ? '"' : "'"
|
||||
return `${quote}${part.value}${quote}`
|
||||
}
|
||||
return String(part?.value)
|
||||
return recastLiteral(part)
|
||||
} else if (part.type === 'Identifier') {
|
||||
return part.name
|
||||
}
|
||||
throw new Error(`Cannot recast ${part}`)
|
||||
}
|
||||
|
||||
function recastLiteral(literal: Literal): string {
|
||||
if (typeof literal.value === 'string') {
|
||||
const quote = literal.raw.trim().startsWith('"') ? '"' : "'"
|
||||
return `${quote}${literal.value}${quote}`
|
||||
}
|
||||
return String(literal?.value)
|
||||
}
|
||||
|
||||
function recastCallExpression(expression: CallExpression): string {
|
||||
return `${expression.callee.name}(${expression.arguments
|
||||
.map(recastArgument)
|
||||
.join(', ')})`
|
||||
}
|
||||
|
||||
function recastArgument(argument: Value): string {
|
||||
if (argument.type === 'Literal') {
|
||||
return recastLiteral(argument)
|
||||
} else if (argument.type === 'Identifier') {
|
||||
return argument.name
|
||||
} else if (argument.type === 'BinaryExpression') {
|
||||
return recastBinaryExpression(argument)
|
||||
} else if (argument.type === 'CallExpression') {
|
||||
return recastCallExpression(argument)
|
||||
} else if (argument.type === 'FunctionExpression') {
|
||||
return recastFunction(argument)
|
||||
}
|
||||
throw new Error(`Cannot recast ${argument}`)
|
||||
}
|
||||
|
||||
function recastFunction(expression: FunctionExpression): string {
|
||||
return `(${expression.params.map((param) => param.name).join(', ')}) => {
|
||||
${recast(expression.body)}
|
||||
}`
|
||||
}
|
||||
|
||||
function recastSketchExpression(
|
||||
expression: SketchExpression,
|
||||
indentation: string
|
||||
): string {
|
||||
return `{
|
||||
${recast(expression.body, '', indentation + ' ')}
|
||||
}`
|
||||
}
|
||||
|
Reference in New Issue
Block a user