@@ -3,14 +3,18 @@ import { BinaryExpression, Expression, isBinaryExpression, isBooleanExpression,
33import { createLoxServices } from "../language-server/lox-module" ;
44import { v4 } from 'uuid' ;
55import { URI } from "vscode-uri" ;
6- import { CancellationToken } from "vscode-languageclient " ;
6+ import { CancellationToken , CancellationTokenSource } from "vscode-languageserver " ;
77
88export interface InterpreterContext {
9- log : ( value : unknown ) => MaybePromise < void >
9+ log : ( value : unknown ) => MaybePromise < void > ,
10+ onStart ?: ( ) => void ,
1011}
1112
1213const services = createLoxServices ( EmptyFileSystem ) ;
1314
15+ // after 5 seconds, the interpreter will be interrupted and call onTimeout
16+ const TIMEOUT_MS = 1000 * 5 ;
17+
1418export async function runInterpreter ( program : string , context : InterpreterContext ) : Promise < void > {
1519 const buildResult = await buildDocument ( program ) ;
1620 try {
@@ -25,7 +29,10 @@ type ReturnFunction = (value: unknown) => void;
2529
2630interface RunnerContext {
2731 variables : Variables ,
28- log : ( value : unknown ) => MaybePromise < void >
32+ cancellationToken : CancellationToken ,
33+ timeout : NodeJS . Timeout ,
34+ log : ( value : unknown ) => MaybePromise < void > ,
35+ onStart ?: ( ) => void ,
2936}
3037
3138class Variables {
@@ -88,17 +95,38 @@ async function buildDocument(program: string): Promise<BuildResult> {
8895 }
8996}
9097
91- async function runProgram ( program : LoxProgram , outerContext : InterpreterContext ) : Promise < void > {
98+ export async function runProgram ( program : LoxProgram , outerContext : InterpreterContext ) : Promise < void > {
99+ const cancellationTokenSource = new CancellationTokenSource ( ) ;
100+ const cancellationToken = cancellationTokenSource . token ;
101+
102+ const timeout = setTimeout ( async ( ) => {
103+ cancellationTokenSource . cancel ( ) ;
104+ } , TIMEOUT_MS ) ;
105+
92106 const context : RunnerContext = {
93107 variables : new Variables ( ) ,
94- log : outerContext . log
108+ cancellationToken,
109+ timeout,
110+ log : outerContext . log ,
111+ onStart : outerContext . onStart ,
95112 } ;
96- context . variables . enter ( ) ;
113+
97114 let end = false ;
115+
116+ context . variables . enter ( ) ;
117+ if ( context . onStart ) {
118+ context . onStart ( ) ;
119+ }
120+
98121 for ( const statement of program . elements ) {
122+ await interruptAndCheck ( context . cancellationToken ) ;
123+
99124 if ( ! isClass ( statement ) && ! isFunctionDeclaration ( statement ) ) {
100125 await runLoxElement ( statement , context , ( ) => { end = true } ) ;
101126 }
127+ else if ( isClass ( statement ) ) {
128+ throw new AstNodeError ( statement , 'Classes are currently unsupported' ) ;
129+ }
102130 if ( end ) {
103131 break ;
104132 }
@@ -107,6 +135,8 @@ async function runProgram(program: LoxProgram, outerContext: InterpreterContext)
107135}
108136
109137async function runLoxElement ( element : LoxElement , context : RunnerContext , returnFn : ReturnFunction ) : Promise < void > {
138+ await interruptAndCheck ( context . cancellationToken ) ;
139+
110140 if ( isExpressionBlock ( element ) ) {
111141 await interruptAndCheck ( CancellationToken . None ) ;
112142 context . variables . enter ( ) ;
@@ -164,6 +194,9 @@ async function runLoxElement(element: LoxElement, context: RunnerContext, return
164194}
165195
166196async function runExpression ( expression : Expression , context : RunnerContext ) : Promise < unknown > {
197+ await interruptAndCheck ( context . cancellationToken ) ;
198+
199+
167200 if ( isBinaryExpression ( expression ) ) {
168201 const { left, right, operator } = expression ;
169202 const rightValue = await runExpression ( right , context ) ;
@@ -244,6 +277,8 @@ async function setExpressionValue(left: Expression, right: unknown, context: Run
244277}
245278
246279async function runMemberCall ( memberCall : MemberCall , context : RunnerContext ) : Promise < unknown > {
280+ await interruptAndCheck ( context . cancellationToken ) ;
281+
247282 let previous : unknown = undefined ;
248283 if ( memberCall . previous ) {
249284 previous = await runExpression ( memberCall . previous , context ) ;
@@ -255,7 +290,7 @@ async function runMemberCall(memberCall: MemberCall, context: RunnerContext): Pr
255290 } else if ( isVariableDeclaration ( ref ) || isParameter ( ref ) ) {
256291 value = context . variables . get ( memberCall , ref . name ) ;
257292 } else if ( isClass ( ref ) ) {
258- throw new AstNodeError ( memberCall , 'Classes are current unsupported' ) ;
293+ throw new AstNodeError ( memberCall , 'Classes are currently unsupported' ) ;
259294 } else {
260295 value = previous ;
261296 }
0 commit comments