summaryrefslogtreecommitdiff
path: root/src/repl.ts
blob: a42183a3e22eda81dd7915154edb496bd1d62d32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import type { TracingLogger } from './utils';
import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';
import { peggyParse } from './parser';
import { evaluate } from './interpreter';

// cool asci logo for CPS
const LOGO = `
 _______   ________   ______   _______   __       
/       \\ /        | /      \\ /       \\ /  |      
$$$$$$$  |$$$$$$$$/ /$$$$$$  |$$$$$$$  |$$ |      
$$ |__$$ |$$ |__    $$ |  $$/ $$ |__$$ |$$ |      
$$    $$< $$    |   $$ |      $$    $$/ $$ |      
$$$$$$$  |$$$$$/    $$ |   __ $$$$$$$/  $$ |      
$$ |  $$ |$$ |_____ $$ \\__/  |$$ |      $$ |_____ 
$$ |  $$ |$$       |$$    $$/ $$ |      $$       |
$$/   $$/ $$$$$$$$/  $$$$$$/  $$/       $$$$$$$$/ 

`;

const HELP = `
  This is the CPS REPL. You can enter CPS programs and see the result of evaluating them.
  This REPL supports multi-line input. To end a multi-line input, enter an empty line.
  
  Commands:
    help - Show this message
    exit - Exit the REPL
  
  About:
    Read "Compiling With Continuations" by Andrew W. Appel for more information about 
    this Intermediate Representation.
  
  Example:
    ~> PRIMOP(+, [INT 1, INT 2], [result], [
     |   APP(LABEL id, [VAR result])
     | ])
`;

export const doRepl = async (
  logger: TracingLogger,
  prompt = 0,
  rl = readline.createInterface({ input, output }),
): Promise<any> => {
  if (prompt === 0) {
    logger.info('welcome to recpl (read eval continue print loop) :)' + LOGO);
  }

  const promptString = `[ ${prompt} ] ~> `;
  const lines: string[] = [await rl.question(promptString)];
  while (lines.at(-1)) {
    const line = lines.at(-1)!;

    if (lines.length === 1 && line === 'help') {
      logger.info(HELP);
      return doRepl(logger, prompt + 1, rl);
    }
    if (line === 'exit') {
      logger.info('Exiting REPL...');
      rl.close();
      return;
    }

    lines.push(
      await rl.question(`|`.padStart(promptString.length - 1, ' ') + ' '),
    );
  }

  const program = lines.slice(0, -1).join('\n');
  try {
    const ast = peggyParse(program);
    logger.debug('AST: ' + JSON.stringify(ast, null, 2));

    const result = await evaluate(ast, logger.createChild('evaluate'));
    logger.info('Result: ' + JSON.stringify(result, null, 2) + '\n');
  } catch (e) {
    logger.error(e!.toString() + '\n');
  }

  return doRepl(logger, prompt + 1, rl);
};