Skip to content

Commit

Permalink
Function call desc (#7372)
Browse files Browse the repository at this point in the history
* Add function call descriptions

* Fix bug

* Fix tests

* Detail with missing results

* Guard against errors

* Print out env to test if it works
  • Loading branch information
iHiD authored Jan 23, 2025
1 parent 2f99a2c commit 8172f02
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 63 deletions.
16 changes: 15 additions & 1 deletion app/javascript/interpreter/expression.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { Token } from './token'
import { Location } from './location'
import { FrameWithResult } from './frames'
import { EvaluationResult } from './evaluation-result'
import {
EvaluationResult,
EvaluationResultCallExpression,
} from './evaluation-result'

function quoteLiteral(value: any): string {
if (typeof value === 'string') {
Expand Down Expand Up @@ -42,6 +45,17 @@ export class CallExpression extends Expression {
) {
super('CallExpression')
}

public description(
result: Partial<'value' | EvaluationResultCallExpression>
): string {
const argsDescription = '()'
return `<code>${
this.callee.name.lexeme
}${argsDescription}</code> (which returned <code>${quoteLiteral(
result.value
)}</code>)`
}
}

export class ArrayExpression extends Expression {
Expand Down
143 changes: 82 additions & 61 deletions app/javascript/interpreter/frames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ export type FrameExecutionStatus = 'SUCCESS' | 'ERROR'
import type {
EvaluationResult,
EvaluationResultChangeVariableStatement,
EvaluationResultIfStatement,
} from './evaluation-result'
import type { ExternalFunction } from './executor'
import {
BinaryExpression,
CallExpression,
Expression,
GroupingExpression,
LiteralExpression,
Expand Down Expand Up @@ -49,33 +51,41 @@ export function describeFrame(
frame: Frame,
externalFunctions: ExternalFunction[]
): string {
// These need to come from the exercise.
const functionDescriptions: Record<string, string> = externalFunctions.reduce(
(acc: Record<string, string>, fn: ExternalFunction) => {
acc[fn.name] = fn.description
return acc
},
{}
)
try {
console.log(process.env.NODE_ENV)
} catch {}
try {
// These need to come from the exercise.
const functionDescriptions: Record<string, string> =
externalFunctions.reduce(
(acc: Record<string, string>, fn: ExternalFunction) => {
acc[fn.name] = fn.description
return acc
},
{}
)

if (!isFrameWithResult(frame)) {
return '<p>There is no information available for this line.</p>'
}
switch (frame.result.type) {
case 'SetVariableStatement':
return describeSetVariableStatement(frame)
case 'ForeachStatement':
return describeForeachStatement(frame)
case 'ChangeVariableStatement':
return describeChangeVariableStatement(frame)
case 'IfStatement':
return describeIfStatement(frame)
case 'ReturnStatement':
return describeReturnStatement(frame)
case 'CallExpression':
return describeCallExpression(frame, functionDescriptions)
default:
return `<p>There is no information available for this line.</p>`
if (!isFrameWithResult(frame)) {
return '<p>There is no information available for this line.</p>'
}
switch (frame.result.type) {
case 'SetVariableStatement':
return describeSetVariableStatement(frame)
case 'ForeachStatement':
return describeForeachStatement(frame)
case 'ChangeVariableStatement':
return describeChangeVariableStatement(frame)
case 'IfStatement':
return describeIfStatement(frame)
case 'ReturnStatement':
return describeReturnStatement(frame)
case 'CallExpression':
return describeCallExpression(frame, functionDescriptions)
default:
return `<p>There is no information available for this line. Show us your code in Discord and we'll improve this!</p>`
}
} catch (e) {
return `<p>There is no information available for this line. Show us your code in Discord and we'll improve this!</p>`
}
}

Expand Down Expand Up @@ -109,36 +119,35 @@ function describeChangeVariableStatement(frame: FrameWithResult): string {
)
}

function describeForeachStatement(frame: FrameWithResult) {
let output = `<p>This looped through <code>${frame.result.iterable.name}</code> array. Each time this line of code is run, it selects the next item from the array and assigns to the <code>${frame.result.elementName}</code> variable.</p>`
function describeForeachStatement(result: EvaluationResult) {
let output = `<p>This looped through <code>${result.iterable.name}</code> array. Each time this line of code is run, it selects the next item from the array and assigns to the <code>${frame.result.elementName}</code> variable.</p>`

if (frame.result.value) {
output += `<p>This iteration set <code>${frame.result.elementName}</code> to:</p>`
output += `<pre><code>${JSON.stringify(
frame.result.value,
null,
2
)}</code></pre>`
if (result.value) {
output += `<p>This iteration set <code>${result.elementName}</code> to:</p>`
output += `<pre><code>${JSON.stringify(result.value, null, 2)}</code></pre>`
}

return output
}

function describeExpression(expression: Expression) {
function describeExpression(expression: Expression, result?: EvaluationResult) {
if (expression instanceof VariableExpression) {
return expression.description()
}
if (expression instanceof LiteralExpression) {
return expression.description()
}
if (expression instanceof CallExpression) {
return expression.description(result)
}
if (expression instanceof GroupingExpression) {
return describeGroupingExpression(expression)
return describeGroupingExpression(expression, result)
}
if (expression instanceof BinaryExpression) {
return describeBinaryExpression(expression)
return describeBinaryExpression(expression, result)
}
if (expression instanceof LogicalExpression) {
return describeLogicalExpression(expression)
return describeLogicalExpression(expression, result)
}
return ''
}
Expand All @@ -164,23 +173,26 @@ function describeOperator(operator: string): string {
return ''
}

function describeBinaryExpression(expression: BinaryExpression): string {
if (expression instanceof BinaryExpression) {
const left = describeExpression(expression.left)
const right = describeExpression(expression.right)
const operator = describeOperator(expression.operator.type)
if (isEqualityOperator(expression.operator.type)) {
return `${left} was ${operator} ${right}`
} else {
return `${left} ${operator} ${right}`
}
function describeBinaryExpression(
expression: BinaryExpression,
result?: EvaluationResult
): string {
const left = describeExpression(expression.left, result?.left)
const right = describeExpression(expression.right, result?.right)
const operator = describeOperator(expression.operator.type)
if (isEqualityOperator(expression.operator.type)) {
return `${left} was ${operator} ${right}`
} else {
return `${left} ${operator} ${right}`
}
return ''
}

function describeLogicalExpression(expression: LogicalExpression): string {
const left = describeExpression(expression.left)
const right = describeExpression(expression.right)
function describeLogicalExpression(
expression: LogicalExpression,
result?: EvaluationResult
): string {
const left = describeExpression(expression.left, result?.left)
const right = describeExpression(expression.right, result?.right)

if (expression.operator.type == 'AND') {
return `both of these were true:</p><ul><li>${left}</li><li>${right}</li></ul><p>`
Expand All @@ -189,21 +201,30 @@ function describeLogicalExpression(expression: LogicalExpression): string {
}
}

function describeGroupingExpression(expression: GroupingExpression): string {
return `${describeExpression(expression.inner)}`
function describeGroupingExpression(
expression: GroupingExpression,
result: EvaluationResult
): string {
return `${describeExpression(expression.inner, result)}`
}

function describeCondition(expression: Expression): string {
return describeExpression(expression)
function describeCondition(
expression: Expression,
result: EvaluationResultIfStatement
): string {
return describeExpression(expression, result.condition)
}

function describeIfStatement(frame: FrameWithResult) {
const ifStatement = frame.context as IfStatement
const conditionDescription = describeCondition(ifStatement.condition)
const conditionDescription = describeCondition(
ifStatement.condition,
frame.result
)
let output = `
<p>This checked whether ${conditionDescription}</p>
<p>The result was <code>${frame.result.value}</code>.</p>
`
<p>This checked whether ${conditionDescription}</p>
<p>The result was <code>${frame.result.value}</code>.</p>
`.trim()
return output
}
function describeReturnStatement(frame: FrameWithResult) {
Expand Down
42 changes: 41 additions & 1 deletion test/javascript/interpreter/expression_descriptions.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
import { interpret } from '@/interpreter/interpreter'
import type { ExecutionContext } from '@/interpreter/executor'
import { LiteralExpression, VariableExpression } from '@/interpreter/expression'
import {
CallExpression,
LiteralExpression,
VariableExpression,
} from '@/interpreter/expression'
import { Location } from '@/interpreter/location'
import { Span } from '@/interpreter/location'
import { type Token, TokenType } from '@/interpreter/token'

const location = new Location(0, new Span(0, 0), new Span(0, 0))

const functionVariableToken: Token = {
lexeme: 'getName',
type: 'FUNCTION',
literal: 'name',
location: location,
}
const parenToken: Token = {
lexeme: '(',
type: 'LEFT_PAREN',
literal: '(',
location: location,
}
const functionVariableExpr = new VariableExpression(
functionVariableToken,
location
)
describe('LiteralExpression', () => {
describe('description', () => {
test('number', () => {
Expand Down Expand Up @@ -42,3 +62,23 @@ describe('VariableExpression', () => {
})
})
})

describe('CallExpression', () => {
describe('description', () => {
test('no args', () => {
const expr = new CallExpression(
functionVariableExpr,
parenToken,
[],
location
)
const result = {
value: 'Jeremy',
}
const actual = expr.description(result)
expect(actual).toBe(
'<code>getName()</code> (which returned <code>"Jeremy"</code>)'
)
})
})
})
Loading

0 comments on commit 8172f02

Please sign in to comment.