inital vesion of recast working

This commit is contained in:
Kurt Hutten IrevDev
2022-11-26 19:03:09 +11:00
parent 48e59ac710
commit 149633a5cb
3 changed files with 140 additions and 19 deletions

View File

@ -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[]

View File

@ -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

View File

@ -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 + ' ')}
}`
}