Skip to content

Commit

Permalink
feat: var declaration init
Browse files Browse the repository at this point in the history
  • Loading branch information
thutasann committed May 27, 2024
1 parent b72f10c commit ab1018f
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 14 deletions.
23 changes: 21 additions & 2 deletions src/ast/compiler/ast.interface.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
/** Abstract Syntax Tree Node Types (Expressions)
/** ## Abstract Syntax Tree Node Types
* ### Defines the structure of our language AST
* @description
* - expression is not a statement
* - expression example -> 5 * 10
* - assignment expression example -> x = 45
* - statements will not return a value
* - statement example -> let x = 45
*/
export type NodeType = 'Program' | 'NumericLiteral' | 'Identifier' | 'BinaryExpr'
export type NodeType =
// STATEMENTS
| 'Program'
| 'VarDeclaration'
// EXPRESSIONS
| 'NumericLiteral'
| 'Identifier'
| 'BinaryExpr'

/** Abstract Statement Interface that include statement kind
* @description
Expand All @@ -25,6 +33,17 @@ export interface IProgram extends IStatement {
body: IStatement[]
}

/** Variable Declaration Node Type
* @example
* let x; // x is undefined
*/
export interface IVarDeclaration extends IStatement {
kind: 'VarDeclaration'
constant: boolean
identifier: string
value?: IExpression
}

/** Expression interface that extends `IStatement` */
export interface IExpression extends IStatement {}

Expand Down
64 changes: 54 additions & 10 deletions src/ast/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
IBinaryExpression,
INumericLiteral,
IIdentifierExpression,
IVarDeclaration,
} from './ast.interface'

/**
Expand Down Expand Up @@ -42,15 +43,58 @@ export class Parser {

/** entry point of the parser*/
private parseSTMT(): IStatement {
return this.parseExpr()
switch (this.at().type) {
case TokenType.Let:
case TokenType.Const:
return this.parseVarDeclaration()
default:
return this.parseExpr()
}
}

/** parse variable declaration
* @example
* - LET IDENTIFIER;
* - ( LET | CONST ) IDENTIFIER = EXPR;
*/
private parseVarDeclaration(): IStatement {
const isConstant = this.eat().type == TokenType.Const
const identifier = this.expect(
TokenType.Identifier,
'Expected identifier name following let | const keywords'
).value

if (this.at().type == TokenType.Semicolon) {
this.eat() // expect semicolon
if (isConstant) {
throw 'Must assign value to constant expression, no value provided.'
}
return {
kind: 'VarDeclaration',
identifier,
constant: false,
} as IVarDeclaration
}

this.expect(TokenType.Equals, 'Expected equals token following identifier in var declaration. ')

const declaration = {
kind: 'VarDeclaration',
value: this.parseExpr(),
constant: isConstant,
} as IVarDeclaration

this.expect(TokenType.Semicolon, 'Variable declaration statement must end with semicolon.') // force end with semicolon

return declaration
}

/** parse expression */
/** ## parse expression */
private parseExpr(): IExpression {
return this.parseAdditiveExpr()
}

/** parse additive expression
/** ### parse additive expression
* @description <br/>
* - Additive Expression has left <-> hand presidence
* @example
Expand All @@ -76,7 +120,7 @@ export class Parser {
return left
}

/** parse multiplicative expression
/** ### parse multiplicative expression
* @description parse `*`, `/` and `%`
*/
private parseMultiplicitaveExpr(): IExpression {
Expand Down Expand Up @@ -120,8 +164,8 @@ export class Parser {
}
}

/** fnc to return tokens index position */
private at(): IToken {
/** fnc to return tokens index position */
return this.tokens[0]
}

Expand All @@ -136,16 +180,16 @@ export class Parser {
return prev
}

/** check token is EOF or not */
private notEOF(): boolean {
return this.tokens[0].type !== TokenType.EOF
}

/** shift the token */
private eat(): IToken {
const prev = this.tokens.shift()
return prev as IToken
}

/** check token is EOF or not */
private notEOF(): boolean {
return this.tokens[0].type !== TokenType.EOF
}
}

/**
Expand Down
7 changes: 6 additions & 1 deletion src/lexer/lexer.interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Token Type Enum
* # Token Type Enum
* @example
* let x = 45 + (foo * bar)
* [letToken, IdentifierToken, EqualsToken, NumberToken]
Expand All @@ -14,12 +14,16 @@ export enum TokenType {
//? ------ Keywords
/** let to declare variable */
Let,
/** const to declare variable */
Const,

//? ------ Grouping * Operators
/** binary operator */
BinaryOperator,
/** equal */
Equals,
/** semicolon */
Semicolon,
/** open parenthesis */
OpenParen,
/** close parenthesis */
Expand All @@ -39,4 +43,5 @@ export interface IToken {
/** Reserved Keywords */
export const KEYWORDS: Record<string, TokenType> = {
let: TokenType.Let,
const: TokenType.Const,
}
4 changes: 3 additions & 1 deletion src/lexer/lexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export class Lexer {
tokens.push(this.token(src.shift(), TokenType.BinaryOperator))
} else if (src[0] == '=') {
tokens.push(this.token(src.shift(), TokenType.Equals))
} else if (src[0] == ';') {
tokens.push(this.token(src.shift(), TokenType.Semicolon))
} else {
// handling multi character tokens
if (this.isInt(src[0])) {
Expand All @@ -35,7 +37,7 @@ export class Lexer {

tokens.push(this.token(num, TokenType.Number))
} else if (this.isAlpha(src[0])) {
let ident = '' // foo let
let ident = '' // foo let (identifier)
while (src.length > 0 && this.isAlpha(src[0])) {
ident += src.shift()
}
Expand Down

0 comments on commit ab1018f

Please sign in to comment.