Author’s note: For ChatGPT tips, scroll to the end. The ChatGPT transcript is here.
I love Crafting Interpreters. I first worked through the book at Recurse Center, next at Bradfield and most recently with David Beazley (repos here, here and here) [1].
The book teaches you how to implement a programming language from scratch. I worked through most of the chapters, but what I enjoy re-implementing is the ‘minimal’ set of features to to generate Fibonacci numbers. It’s a nice use case, requiring built-in operations, control flow and recursive function calls.
Since the code has gone through a couple of iterations, I was curious to see how ChatGPT could help me improve code quality further.
The scanner (or lexer) converts the source code into tokens (truncated to ease viewing).
> source_code = """\\
func fibonacci(n) {
if (n < 2) {
return 1;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
fibonacci(9);"""
> tokens = scanner.scan(source_code)
> for token in tokens: print(token)
Token(token_type=<TokenType.FUNC: 'FUNC'>, value='func', line=1)
Token(token_type=<TokenType.NAME: 'NAME'>, value='fibonacci', line=1)
Token(token_type=<TokenType.PAREN_LEFT: 'PAREN_LEFT'>, value='(', line=1)
Token(token_type=<TokenType.NAME: 'NAME'>, value='n', line=1)
Token(token_type=<TokenType.PAREN_RIGHT: 'PAREN_RIGHT'>, value=')', line=1)
...
Token(token_type=<TokenType.NAME: 'NAME'>, value='fibonacci', line=8)
Token(token_type=<TokenType.PAREN_LEFT: 'PAREN_LEFT'>, value='(', line=8)
Token(token_type=<TokenType.INTEGER: 'INTEGER'>, value='9', line=8)
Token(token_type=<TokenType.PAREN_RIGHT: 'PAREN_RIGHT'>, value=')', line=8)
Token(token_type=<TokenType.SEMICOLON: 'SEMICOLON'>, value=';', line=8)
Token(token_type=<TokenType.EOF: 'EOF'>, value='EOF', line=8)
Next the parser converts the tokens into abstract syntax trees (hereafter, ASTs).
> statements = parser.parse(tokens)
> for statement in statements: print(statement)
Function(
name=Name(text='fibonacci'),
parameters=[Name(text='n')],
body=Block(
statements=[
...
]
)
)
Expression(
expression=Call(
callee=Name(text='fibonacci'),
arguments=[Integer(value='9')]
)
)
Finally the interpreter ‘executes’ the ASTs to generate the desired output.
> results = interpreter.interpret(statements)
> for result in results: print(result)
None
55
To separate ChatGPT changes from my own implementation, I copied over the latest version of minimal-lox
into a new repo simple-lox
. Naturally I got ChatGPT to suggest the name.