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'
|
type: 'CallExpression'
|
||||||
callee: Identifier
|
callee: Identifier
|
||||||
arguments: Value[]
|
arguments: Value[]
|
||||||
@ -244,7 +244,7 @@ function makeVariableDeclaration(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Value =
|
export type Value =
|
||||||
| Literal
|
| Literal
|
||||||
| Identifier
|
| Identifier
|
||||||
| BinaryExpression
|
| BinaryExpression
|
||||||
@ -371,7 +371,7 @@ export type BinaryPart = Literal | Identifier
|
|||||||
// | LogicalExpression
|
// | LogicalExpression
|
||||||
// | ConditionalExpression
|
// | ConditionalExpression
|
||||||
|
|
||||||
interface Literal extends GeneralStatement {
|
export interface Literal extends GeneralStatement {
|
||||||
type: 'Literal'
|
type: 'Literal'
|
||||||
value: string | number | boolean | null
|
value: string | number | boolean | null
|
||||||
raw: string
|
raw: string
|
||||||
@ -459,7 +459,7 @@ function makeBinaryExpression(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SketchExpression extends GeneralStatement {
|
export interface SketchExpression extends GeneralStatement {
|
||||||
type: 'SketchExpression'
|
type: 'SketchExpression'
|
||||||
body: BlockStatement
|
body: BlockStatement
|
||||||
}
|
}
|
||||||
@ -483,7 +483,7 @@ function makeSketchExpression(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FunctionExpression extends GeneralStatement {
|
export interface FunctionExpression extends GeneralStatement {
|
||||||
type: 'FunctionExpression'
|
type: 'FunctionExpression'
|
||||||
id: Identifier | null
|
id: Identifier | null
|
||||||
params: Identifier[]
|
params: Identifier[]
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import { recast } from './recast'
|
import { recast } from './recast'
|
||||||
import { Program } from './abstractSyntaxTree'
|
import { Program, abstractSyntaxTree } from './abstractSyntaxTree'
|
||||||
import { abstractSyntaxTree } from './abstractSyntaxTree'
|
import { lexer, Token } from './tokeniser'
|
||||||
import { lexer } from './tokeniser'
|
import fs from 'node:fs'
|
||||||
import { Token } from './tokeniser'
|
|
||||||
|
|
||||||
describe('recast', () => {
|
describe('recast', () => {
|
||||||
it('recasts a simple program', () => {
|
it('recasts a simple program', () => {
|
||||||
@ -26,6 +25,56 @@ describe('recast', () => {
|
|||||||
const { ast: ast2 } = code2ast(codeWithOtherQuotes)
|
const { ast: ast2 } = code2ast(codeWithOtherQuotes)
|
||||||
expect(recast(ast2)).toBe(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
|
// 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
|
return ast.body
|
||||||
.map((statement) => {
|
.map((statement) => {
|
||||||
if (statement.type === 'ExpressionStatement') {
|
if (statement.type === 'ExpressionStatement') {
|
||||||
if (statement.expression.type === 'BinaryExpression') {
|
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') {
|
} else if (statement.type === 'VariableDeclaration') {
|
||||||
return statement.declarations
|
return statement.declarations
|
||||||
.map((declaration) => {
|
.map((declaration) => {
|
||||||
if (declaration.init.type === 'BinaryExpression') {
|
if (declaration.init.type === 'BinaryExpression') {
|
||||||
return `${statement.kind} ${
|
return `${indentation}${statement.kind} ${
|
||||||
declaration.id.name
|
declaration.id.name
|
||||||
} = ${recastBinaryExpression(declaration.init)}`
|
} = ${recastBinaryExpression(declaration.init)}`
|
||||||
} else if (declaration.init.type === 'Literal') {
|
} 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 ''
|
return ''
|
||||||
})
|
})
|
||||||
.join('')
|
.join('')
|
||||||
|
} else if (statement.type === 'ReturnStatement') {
|
||||||
|
return `${indentation}return ${recastArgument(statement.argument)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return statement.type
|
return statement.type
|
||||||
})
|
})
|
||||||
.join('\n')
|
.join('\n')
|
||||||
@ -34,13 +66,53 @@ function recastBinaryExpression(expression: BinaryExpression): string {
|
|||||||
|
|
||||||
function recastBinaryPart(part: BinaryPart): string {
|
function recastBinaryPart(part: BinaryPart): string {
|
||||||
if (part.type === 'Literal') {
|
if (part.type === 'Literal') {
|
||||||
if (typeof part.value === 'string') {
|
return recastLiteral(part)
|
||||||
const quote = part.raw.includes('"') ? '"' : "'"
|
|
||||||
return `${quote}${part.value}${quote}`
|
|
||||||
}
|
|
||||||
return String(part?.value)
|
|
||||||
} else if (part.type === 'Identifier') {
|
} else if (part.type === 'Identifier') {
|
||||||
return part.name
|
return part.name
|
||||||
}
|
}
|
||||||
throw new Error(`Cannot recast ${part}`)
|
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