Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
TheTrio committed Jul 30, 2024
1 parent ff3b9b1 commit 64e18fb
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 59 deletions.
52 changes: 34 additions & 18 deletions src/evaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ import {
Value,
WhileExpression,
} from './types'
import { isTruthy, throwIfInvalidArrayIndexAccess } from './utils'
import {
isTruthy,
throwIfInvalidArrayIndexAccess,
toPositiveIndex,
} from './utils'

export class Evaluator {
statements: Statement[]
Expand All @@ -33,8 +37,7 @@ export class Evaluator {
this.store = store
}
evaluate() {
const result = evaluateStatements(this.statements, this.store)
return result
return evaluateStatements(this.statements, this.store)
}
}

Expand Down Expand Up @@ -97,24 +100,24 @@ const getValueFromLValue = (lvalue: LValue, store: Store) => {
}

const getValueFromIndexExpression = (lvalue: IndexExpression, store: Store) => {
const array = evaluateExpression(lvalue.left, store)
const index = evaluateExpression(lvalue.index, store)
if (!(array instanceof ArrayObject)) {
throw new Error('Trying to index a non-array object')
}
if (typeof index !== 'number') {
throw new Error('Index must be a number')
}
if (index < 0 || index >= array.elements.length) {
throw new Error('Index out of bounds')
}
const array: ArrayObject = evaluateExpression(lvalue.left, store)
const index = toPositiveIndex(
evaluateExpression(lvalue.index, store),
array._length()
)

throwIfInvalidArrayIndexAccess(array, index)
return array.elements.at(index)
}

const setValueFromLValue = (lvalue: LValue, store: Store, value: any) => {
if (lvalue instanceof IndexExpression) {
const array = evaluateExpression(lvalue.left, store)
const index = evaluateExpression(lvalue.index, store)
const array: ArrayObject = evaluateExpression(lvalue.left, store)
let index = toPositiveIndex(
evaluateExpression(lvalue.index, store),
array._length()
)

throwIfInvalidArrayIndexAccess(array, index)
array.elements[index] = value
} else {
Expand All @@ -134,6 +137,7 @@ const evaluateExpression = (
if (expression === null) {
return null
}

switch (typeof expression) {
case 'string':
case 'number':
Expand All @@ -149,8 +153,12 @@ const evaluateExpression = (
}

if (expression instanceof IndexExpression) {
const array = evaluateExpression(expression.left, store)
const index = evaluateExpression(expression.index, store)
const array: ArrayObject = evaluateExpression(expression.left, store)
const index = toPositiveIndex(
evaluateExpression(expression.index, store),
array._length()
)

throwIfInvalidArrayIndexAccess(array, index)
if (array instanceof ArrayObject && typeof index === 'number') {
return array.elements.at(index)
Expand All @@ -160,9 +168,11 @@ const evaluateExpression = (
if (expression instanceof BinaryExpression) {
return evaluateBinaryExpression(expression, store)
}

if (expression instanceof UnaryExpression) {
return evaluateUnaryExpression(expression, store)
}

if (expression instanceof IfElseExpression) {
const blockStore = new Store(store)
const result = evaluateIfElseExpression(expression, blockStore)
Expand All @@ -171,6 +181,7 @@ const evaluateExpression = (
}
return result
}

if (expression instanceof BlockExpression) {
const blockStore = new Store(store)
return extractReturnValue(
Expand Down Expand Up @@ -262,10 +273,12 @@ const evaluateFunction = (
if (func instanceof NativeFunction) {
return func.fn(...evaluatedArgs)
}

const newStore = new Store(func.store)
func.parameters.forEach((param, i) => {
newStore.set(param.value!, evaluatedArgs[i])
})

return extractReturnValue(
evaluateBlockStatements(func.body, newStore)!,
newStore
Expand Down Expand Up @@ -296,6 +309,7 @@ const evaluateBinaryExpression = (
): number | boolean | string | undefined => {
const left = evaluateExpression(expression.left, store)
const right = evaluateExpression(expression.right, store)

switch (expression.node.type) {
case TokenType.PLUS:
if (typeof left === 'number' && typeof right === 'number') {
Expand Down Expand Up @@ -367,6 +381,7 @@ const evaluateBinaryExpression = (
}
throw new TypeMismatchError(TokenType.NOT_EQ, typeof left, typeof right)
}
throw new Error('Unknown binary expression')
}

const evaluateUnaryExpression = (
Expand All @@ -386,6 +401,7 @@ const evaluateUnaryExpression = (
}
break
}
throw new Error('Unknown unary expression')
}

const evaluateIfElseExpression = (
Expand Down
14 changes: 7 additions & 7 deletions src/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { isDigit, isLetter, isWhitespace } from './utils'
export class Lexer {
position: number
input: string

constructor(input: string) {
this.input = input
this.position = 0
Expand All @@ -27,14 +28,12 @@ export class Lexer {
nextToken(): Token {
let token = TokenType.ILLEGAL

// first check for two character tokens
const twoCharToken = this.twoCharToken()

if (twoCharToken) {
return twoCharToken
}

// then check for single character tokens
switch (this.currentChar()) {
case '\0':
token = TokenType.EOF
Expand Down Expand Up @@ -130,7 +129,8 @@ export class Lexer {
type: token,
}
}
getString() {

private getString() {
let str = ''
while (this.currentChar() !== '"' && this.currentChar() !== '\0') {
str += this.currentChar()
Expand All @@ -143,7 +143,7 @@ export class Lexer {
return str
}

getIdentifier() {
private getIdentifier() {
let identifier = ''
while (
isLetter(this.currentChar()) ||
Expand All @@ -155,7 +155,7 @@ export class Lexer {
return identifier
}

getNumber() {
private getNumber() {
let number = ''
while (isDigit(this.currentChar())) {
number += this.currentChar()
Expand All @@ -164,15 +164,15 @@ export class Lexer {
return number
}

peakChar() {
private peakChar() {
const nextPos = this.position + 1
if (nextPos >= this.input.length) {
return '\0'
}
return this.input[nextPos]
}

twoCharToken() {
private twoCharToken() {
if (this.currentChar() === '=') {
if (this.peakChar() === '=') {
this.position += 2
Expand Down
55 changes: 21 additions & 34 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ import { getPrecedence, isBinaryOperator, isLVal } from './utils'
export const PRECEDENCES = {
[TokenType.PLUS]: 1,
[TokenType.MINUS]: 1,
[TokenType.ASTERISK]: 2,
[TokenType.SLASH]: 2,
[TokenType.LEFT_PAREN]: 3,
[TokenType.LBRACKET]: 3,
[TokenType.ASSIGN]: 1,
[TokenType.LESS_THAN]: 1,
[TokenType.GREATER_THAN]: 1,
[TokenType.LESS_THAN_EQ]: 1,
[TokenType.GREATER_THAN_EQ]: 1,
[TokenType.EQ]: 1,
[TokenType.NOT_EQ]: 1,
[TokenType.ASTERISK]: 2,
[TokenType.SLASH]: 2,
[TokenType.LEFT_PAREN]: 3,
[TokenType.LBRACKET]: 3,
}

export class Parser {
Expand Down Expand Up @@ -155,7 +155,7 @@ export class Parser {
}
}

parseLetStatement() {
private parseLetStatement() {
this.match(TokenType.LET)
const letStatement = new LetStatement()
letStatement.lvalue = this.match(TokenType.IDENT)!
Expand All @@ -167,28 +167,21 @@ export class Parser {
return letStatement
}

parseReassignStatement() {
const lvalue = this.match(TokenType.IDENT)!
this.match(TokenType.ASSIGN)
const rvalue = this.parseExpression()
this.match(TokenType.SEMICOLON)
return new ReassignmentStatement(lvalue, rvalue!)
}

parseReturnStatement() {
private parseReturnStatement() {
this.match(TokenType.RETURN)
const returnStatement = new ReturnStatement(this.parseExpression()!)
this.match(TokenType.SEMICOLON)
return returnStatement
}

parseExpression(parentPrecedence: number = 0): Expression | null {
private parseExpression(parentPrecedence: number = 0): Expression | null {
let leftExpression: Expression | null = null

const prefixFunction =
this.PREFIX_FUNCTIONS[
this.currentToken()!.type as keyof typeof this.PREFIX_FUNCTIONS
]

if (prefixFunction) {
leftExpression = prefixFunction()
} else {
Expand Down Expand Up @@ -247,40 +240,41 @@ export class Parser {
return leftExpression!
}

parseIntegerLiteral(): Expression {
private parseIntegerLiteral(): Expression {
return {
node: this.match(TokenType.INT)!,
}
}

parsePrefixExpression(): Expression {
private parsePrefixExpression(): Expression {
const token = this.currentToken()
this.position++
return new UnaryExpression(
token!,
this.parseExpression(PRECEDENCES[token?.type as keyof typeof PRECEDENCES])
)
}
parseIdentifier(): Expression {

private parseIdentifier(): Expression {
return {
node: this.match(TokenType.IDENT)!,
}
}

parseBoolean(): Expression {
private parseBoolean(): Expression {
return {
node: this.match(this.currentToken()!.type)!,
}
}

parseGroupedExpression(): Expression {
private parseGroupedExpression(): Expression {
this.match(TokenType.LEFT_PAREN)
const expression = this.parseExpression()
this.match(TokenType.RIGHT_PAREN)
return expression!
}

parseIfExpression(): Expression {
private parseIfExpression(): Expression {
const token = this.match(TokenType.IF)
this.match(TokenType.LEFT_PAREN)
const condition = this.parseExpression()
Expand All @@ -299,7 +293,7 @@ export class Parser {
return new IfElseExpression(token!, condition, consequence, [])
}

parseBlockStatement() {
private parseBlockStatement() {
const statements: Statement[] = []
while (
this.currentToken() &&
Expand All @@ -316,7 +310,7 @@ export class Parser {
return statements
}

parseBlockExpression(): Expression | null {
private parseBlockExpression(): Expression | null {
this.match(TokenType.LBRACE)
const statements: Statement[] = []
while (
Expand All @@ -333,7 +327,7 @@ export class Parser {
return new BlockExpression(statements)
}

parseFunctions(): Expression {
private parseFunctions(): Expression {
const funcToken = this.match(TokenType.FUNCTION)
this.match(TokenType.LEFT_PAREN)
const parameters = this.parseFunctionParameters()
Expand All @@ -345,7 +339,7 @@ export class Parser {
return new FunctionExpression(parameters, body, funcToken!)
}

parseCallArguments() {
private parseCallArguments() {
const args: Expression[] = []
while (
this.currentToken()?.type !== TokenType.RIGHT_PAREN &&
Expand All @@ -365,7 +359,7 @@ export class Parser {
return args
}

parseFunctionParameters() {
private parseFunctionParameters() {
const parameters: Token[] = []
while (
this.currentToken()?.type !== TokenType.RIGHT_PAREN &&
Expand All @@ -385,7 +379,7 @@ export class Parser {
return parameters
}

match(tokenType: TokenType) {
private match(tokenType: TokenType) {
if (this.currentToken()?.type === tokenType) {
const token = this.currentToken()
this.position++
Expand All @@ -395,11 +389,4 @@ export class Parser {
`expected ${tokenType} but got ${this.currentToken()?.type}`
)
}

get peekToken() {
if (this.position + 1 >= this.tokens.length) {
return null
}
return this.tokens[this.position + 1]
}
}
7 changes: 7 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,10 @@ export const throwIfInvalidArrayIndexAccess = (array: any, index: any) => {
}
return true
}

export const toPositiveIndex = (index: number, length: number) => {
if (index < 0) {
return length + index
}
return index
}

0 comments on commit 64e18fb

Please sign in to comment.