diff options
Diffstat (limited to 'the_e_programming_language')
-rw-r--r-- | the_e_programming_language/ast.json | 79 | ||||
-rw-r--r-- | the_e_programming_language/ast.ts | 357 | ||||
-rw-r--r-- | the_e_programming_language/code.txt | 11 | ||||
-rw-r--r-- | the_e_programming_language/compiler.ts | 151 | ||||
-rwxr-xr-x | the_e_programming_language/lang.ts | 31 | ||||
-rwxr-xr-x | the_e_programming_language/test-2.e | 6 | ||||
-rwxr-xr-x | the_e_programming_language/test.e | 43 | ||||
-rwxr-xr-x | the_e_programming_language/test.e.txt | 43 | ||||
-rwxr-xr-x | the_e_programming_language/tokenizer.ts | 171 |
9 files changed, 0 insertions, 892 deletions
diff --git a/the_e_programming_language/ast.json b/the_e_programming_language/ast.json deleted file mode 100644 index faf9b67..0000000 --- a/the_e_programming_language/ast.json +++ /dev/null @@ -1,79 +0,0 @@ -[ - { - "type": "VariableDeclaration", - "identifier": "buff", - "value": { - "type": "Number", - "value": 0 - }, - "vtype": "int", - "length": 4 - }, - { - "type": "VariableDeclaration", - "identifier": "counter", - "value": { - "type": "Number", - "value": 0 - }, - "vtype": "int", - "length": 1 - }, - { - "type": "FunctionDeclaration", - "name": "_start", - "body": [ - { - "type": "While", - "condition": { - "type": "BinaryExpression", - "operator": "<", - "left": { - "type": "Identifier", - "name": "counter" - }, - "right": { - "type": "Number", - "value": 4 - } - }, - "branch": [ - { - "type": "Assignment", - "identifier": { - "type": "Identifier", - "name": "buff", - "offset": { - "type": "Identifier", - "name": "counter" - } - }, - "value": { - "type": "Identifier", - "name": "counter" - } - }, - { - "type": "Assignment", - "identifier": { - "type": "Identifier", - "name": "counter" - }, - "value": { - "type": "BinaryExpression", - "operator": "+", - "left": { - "type": "Identifier", - "name": "counter" - }, - "right": { - "type": "Number", - "value": 1 - } - } - } - ] - } - ] - } -] \ No newline at end of file diff --git a/the_e_programming_language/ast.ts b/the_e_programming_language/ast.ts deleted file mode 100644 index 80210d8..0000000 --- a/the_e_programming_language/ast.ts +++ /dev/null @@ -1,357 +0,0 @@ -import { Token, TokenType } from "./tokenizer.ts"; - -export interface ASTNode { - type: string; -} - -export interface VariableDeclarationNode extends ASTNode { - type: "VariableDeclaration"; - identifier: string; - value: ASTNode; - vtype: string; - length: number; -} - -export interface FunctionDeclarationNode extends ASTNode { - type: "FunctionDeclaration"; - name: string; - // params: string[]; - body: ASTNode[]; -} - -export interface AssignmentNode extends ASTNode { - type: "Assignment"; - identifier: IdentifierNode; - value: ASTNode; -} - -export interface BinaryExpressionNode extends ASTNode { - type: "BinaryExpression"; - operator: string; - left: ASTNode; - right: ASTNode; -} - -export interface LiteralNode extends ASTNode { - type: "Literal"; - value: string; -} - -export interface NumberNode extends ASTNode { - type: "Number"; - value: number; -} - -export interface IdentifierNode extends ASTNode { - type: "Identifier"; - name: string; - offset?: ASTNode; -} - -export interface FunctionCallNode extends ASTNode { - type: "FunctionCall"; - identifier: string; - args: ASTNode[]; -} - -// export interface BranchFunctionCallNode extends ASTNode { -// type: "BranchFunctionCall"; -// identifier: string; -// args: ASTNode[]; -// branches: ASTNode[][]; -// } - -// export interface StartBlockNode extends ASTNode { -// type: "StartBlock"; -// body: ASTNode[]; -// } - -export interface IfNode extends ASTNode { - type: "If"; - condition: ASTNode; - thenBranch: ASTNode[]; - elseBranch?: ASTNode[]; -} - -export interface WhileNode extends ASTNode { - type: "While"; - condition: ASTNode; - branch: ASTNode[]; -} - -// export interface ForNode extends ASTNode { -// type: "For"; -// times: ASTNode; -// varname: ASTNode; -// branch: ASTNode[]; -// } - -// export interface GreenFlagNode extends ASTNode { -// type: "GreenFlag"; -// branch: ASTNode[]; -// } - -// use 1 or 0 for boolean -// export interface BooleanNode extends ASTNode { -// type: "Boolean"; -// value: boolean; -// } - -// export interface IncludeNode extends ASTNode { -// type: "Include"; -// itype: string; -// path: string; -// } - -// export interface ListDeclarationNode extends ASTNode { -// type: "ListDeclaration"; -// identifier: string; -// value: ASTNode[]; -// vtype: 'list' | 'global' -// } - -export default class AST { - private tokens: Token[]; - position: number = 0; - - constructor(tokens: Token[]) { - this.tokens = tokens; - } - - private peek(ahead = 0): Token { - return this.tokens[this.position + ahead]; - } - - private advance(): Token { - return this.tokens[this.position++]; - } - - private match(...types: TokenType[]): boolean { - if (types.includes(this.peek().type)) { - this.advance(); - return true; - } - return false; - } - - private matchTk(types: TokenType[], token = this.peek()): boolean { - if (types.includes(token.type)) { - return true; - } - return false; - } - - private expect(type: TokenType, errorMessage: string): Token { - if (this.peek().type === type) { - return this.advance(); - } - console.error('trace: tokens', this.tokens, '\nIDX:', this.position); - throw new Error(errorMessage); - } - - parse(): ASTNode[] { - const nodes: ASTNode[] = []; - while (this.peek().type !== TokenType.EOF) { - nodes.push(this.parseStatement()); - } - return nodes; - } - - private parseStatement(): ASTNode { - if (this.matchTk([TokenType.TYPE])) { - const type = this.advance().value - let len = 1; - if (this.match(TokenType.LBRACKET)) { - len = Number(this.expect(TokenType.NUMBER, 'expected number after [').value); - this.expect(TokenType.RBRACKET, 'expected ] after length') - } - const identifier = this.expect(TokenType.IDENTIFIER, "expected var name after type (hint: functions dont have return types yet").value; - this.expect(TokenType.ASSIGN, "expected = after var name"); - const value = this.parseAssignment(false); - return { type: "VariableDeclaration", identifier, value, vtype: type, length: len } as VariableDeclarationNode; - } - - if (this.match(TokenType.FN_DECL)) { - const name = this.expect(TokenType.IDENTIFIER, "expected function name after fn").value; - // this.expect(TokenType.LPAREN, "Expected '(' after function name"); - // const params: string[] = []; - // if (!this.match(TokenType.RPAREN)) { - // do { - // params.push(this.expect(TokenType.IDENTIFIER, "Expected parameter name").value); - // } while (this.match(TokenType.COMMA)); - // this.expect(TokenType.RPAREN, "Expected ')' after parameters"); - // } - this.expect(TokenType.LBRACE, "expected '{' before function body"); - const body = this.parseBlock(); - return { type: "FunctionDeclaration", name, body } as FunctionDeclarationNode; - } - - if (this.match(TokenType.IF)) { - this.expect(TokenType.LPAREN, "Expected '(' after 'if'"); - const condition = this.parseAssignment(); - this.expect(TokenType.RPAREN, "Expected ')' after if condition"); - this.expect(TokenType.LBRACE, "Expected '{' after if condition"); - const thenBranch = this.parseBlock(); - let elseBranch: ASTNode[] | undefined; - if (this.match(TokenType.ELSE)) { - this.expect(TokenType.LBRACE, "Expected '{' after 'else'"); - elseBranch = this.parseBlock(); - } - return { type: "If", condition, thenBranch, elseBranch } as IfNode; - } - - if (this.match(TokenType.WHILE)) { - this.expect(TokenType.LPAREN, "Expected '(' after 'while'"); - const condition = this.parseAssignment(); - this.expect(TokenType.RPAREN, "Expected ')' after while condition"); - this.expect(TokenType.LBRACE, "Expected '{' after while condition"); - const branch = this.parseBlock(); - return { type: "While", condition, branch } as WhileNode; - } - - // if (this.match(TokenType.FOR)) { - // this.expect(TokenType.LPAREN, "Expected '(' after 'for'"); - // const varname = this.parseAssignment(); - // const of = this.expect(TokenType.IDENTIFIER, 'expected of'); - // if (of.value !== 'of') throw new Error('expected of'); - // const times = this.parseAssignment(); - // this.expect(TokenType.RPAREN, "Expected ')' after for"); - // this.expect(TokenType.LBRACE, "Expected '{' after for"); - // const branch = this.parseBlock(); - - // return { type: "For", varname, times, branch } as ForNode; - // } - - // if (this.match(TokenType.GREENFLAG)) { - // this.expect(TokenType.LBRACE, "Expected '{' after greenflag"); - // const branch = this.parseBlock(); - - // return { type: "GreenFlag", branch } as GreenFlagNode; - // } - - return this.parseAssignment(); - } - - private parseBlock(): ASTNode[] { - const nodes: ASTNode[] = []; - - while (!this.match(TokenType.RBRACE)) { - nodes.push(this.parseStatement()); - } - - return nodes; - } - - private parseAssignment(allowStuff = true): ASTNode { - - const expr = this.parseBinaryExpression(allowStuff); - if (this.match(TokenType.ASSIGN)) { - if (expr.type !== "Identifier") - throw new Error("invalid assignment target; expected an identifier"); - const value = allowStuff ? this.parseAssignment() : this.parsePrimary(false); - // let offset = undefined; - // if (this.match(TokenType.LBRACKET)) { - // offset = this.parseAssignment(); - // this.expect(TokenType.RBRACKET, 'expected ]') - // } - return { type: "Assignment", identifier: (expr as IdentifierNode), value } as AssignmentNode; - } - return expr; - } - - private parseBinaryExpression(allowStuff = false): ASTNode { - let left = this.parseCall(allowStuff); - - while (this.peek().type === TokenType.BINOP) { - const operator = this.advance().value; - const right = this.parseCall(); - left = { type: "BinaryExpression", operator, left, right } as BinaryExpressionNode; - } - return left; - } - - private parseCall(allowStuff = false): ASTNode { - let expr = this.parsePrimary(allowStuff); - - while (this.peek().type === TokenType.LPAREN) { - expr = this.finishCall(expr); - } - return expr; - } - - private finishCall(callee: ASTNode): ASTNode { - this.expect(TokenType.LPAREN, "Expected '(' after function name"); - //TODO - arguments - // const args: ASTNode[] = []; - // if (this.peek().type !== TokenType.RPAREN) { - // do { - // args.push(this.parseAssignment()); - // } while (this.match(TokenType.COMMA)); - // } - this.expect(TokenType.RPAREN, "Expected ')' after arguments"); - - - // if (this.peek().type === TokenType.LBRACE) { - // const branches: ASTNode[][] = []; - // do { - // this.expect(TokenType.LBRACE, "Expected '{' for branch block"); - // branches.push(this.parseBlock()); - // } while (this.peek().type === TokenType.LBRACE); - - // if (callee.type !== "Identifier") - // throw new Error("Branch function call expects an identifier"); - // return { - // type: "BranchFunctionCall", - // identifier: (callee as IdentifierNode).name, - // args, - // branches, - // } as BranchFunctionCallNode; - // } - - - if (callee.type !== "Identifier") - throw new Error("Function call expects an identifier"); - return { - type: "FunctionCall", - identifier: (callee as IdentifierNode).name, - // args, - } as FunctionCallNode; - } - - private parsePrimary(allowOther = true): ASTNode { - const token = this.peek(); - - if (this.match(TokenType.NUMBER)) { - return { type: "Number", value: Number(token.value) } as NumberNode; - } - - if (this.match(TokenType.LITERAL)) { - return { type: "Literal", value: token.value } as LiteralNode; - } - - if (this.match(TokenType.IDENTIFIER) && allowOther) { - - // if (["True", "true", "False", "false"].includes(token.value)) { - // return { - // type: "Boolean", - // value: token.value === "True" || token.value === "true" - // } as BooleanNode; - // } - let offset = undefined; - if (this.match(TokenType.LBRACKET)) { - offset = this.parseAssignment(); - this.expect(TokenType.RBRACKET, 'expected ]') - } - return { type: "Identifier", name: token.value, offset } as IdentifierNode; - } - - if (this.match(TokenType.LPAREN) && allowOther) { - const expr = this.parseAssignment(); - this.expect(TokenType.RPAREN, "Expected ')' after expression"); - return expr; - } - - throw new Error(`Unexpected token: ${token.type}`); - } - -} \ No newline at end of file diff --git a/the_e_programming_language/code.txt b/the_e_programming_language/code.txt deleted file mode 100644 index f45a592..0000000 --- a/the_e_programming_language/code.txt +++ /dev/null @@ -1,11 +0,0 @@ -mov 97,0 -mov 98,0 -str 98,97 -mov 97,0 -mov 98,4 -str 98,97 -pop 97 -mov 98,0 -cmp 97,98 -mov 97,18 -jnz 97 \ No newline at end of file diff --git a/the_e_programming_language/compiler.ts b/the_e_programming_language/compiler.ts deleted file mode 100644 index 37538f7..0000000 --- a/the_e_programming_language/compiler.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { ASTNode, BinaryExpressionNode, FunctionDeclarationNode, NumberNode, VariableDeclarationNode, WhileNode } from "./ast.ts"; -// import { PC } from "../pc.ts"; -// const pc = new PC(); - -type Opcode = - 'mov' | - 'swp' | - 'ld' | - 'str' | - 'add' | - 'sub' | - 'mul' | - 'div' | - 'mod' | - 'shl' | - 'shr' | - 'cmp' | - 'cmr' | - 'and' | - 'or' | - 'xor' | - 'not' | - 'push' | - 'pop' | - 'halt' | - 'sys' | - 'jmp' | - 'jnz' | - 'jz' | - 'jmr' | - 'ret' | - 'end'; -type Register = 97 | 98 | 99 | 100 - -interface Instruction { - opcode: Opcode, - args: (Register | number)[] -} - -const types: Record<string, number> = { - 'int' : 1, - 'bool': 1, - 'char': 1 -} - -const A: Register = 97; -const B: Register = 98; -// deno-lint-ignore no-unused-vars -const C: Register = 99; -// deno-lint-ignore no-unused-vars -const D: Register = 100; - -export default class Compiler { - vars: Record<string, [number, number]> = {}; - functions: Record<string, number> = {}; - AST: ASTNode[]; - lastAddr: number = 0; - instructions: Instruction[] = []; - constructor (ast: ASTNode[]) { - this.AST = ast - } - compile (node: ASTNode) { - if ((node as VariableDeclarationNode).type == 'VariableDeclaration') { - const varDeclNode = node as VariableDeclarationNode; - if (!types[varDeclNode.vtype]) throw 'unknown type'; - const addr = this.vars[varDeclNode.identifier] = - [this.lastAddr, types[varDeclNode.vtype] * varDeclNode.length]; - this.lastAddr += types[varDeclNode.vtype] * varDeclNode.length; - if (varDeclNode.value.type != 'Number') throw 'a'; - this.instructions.push({ - opcode: 'mov', - args: [A, (varDeclNode.value as NumberNode).value] - }) - this.instructions.push({ - opcode: 'mov', - args: [B, addr[0]] - }) - this.instructions.push({ - opcode: 'str', - args: [B, A] - }) - } else if ((node as FunctionDeclarationNode).type == 'FunctionDeclaration') { - const fnDeclNode = node as FunctionDeclarationNode; - this.functions[fnDeclNode.name] = this.instructions - .map(k => 1 + k.args.length) - .reduce((prev, curr) => { - return prev + curr - }, 0); - for (const node of fnDeclNode.body) { - this.compile(node) - } - } else if ((node as BinaryExpressionNode).type == 'BinaryExpression') { - const binExpNode = node as BinaryExpressionNode; - this.instructions.push({ - opcode: 'pop', - args: [A] - }) - this.instructions.push({ - opcode: 'pop', - args: [B] - }) - switch (binExpNode.operator) { - case '+': - this.instructions.push({ - opcode: 'add', - args: [A, A, B] - }) - break; - - default: - throw 'oh no' - } - this.instructions.push({ - opcode: 'push', - args: [A] - }) - } else if ((node as WhileNode).type == 'While') { - const whileNode = node as WhileNode; - const start = this.instructions - .map(k => 1 + k.args.length) - .reduce((prev, curr) => { - return prev + curr - }, 0); - for (const node of whileNode.branch) { - this.compile(node) - } - this.instructions.push({ - opcode: 'pop', - args: [A] - }) - this.instructions.push({ - opcode: 'mov', - args: [B, 0] - }) - this.instructions.push({ - opcode: 'cmp', - args: [A, B] - }) - this.instructions.push({ - opcode: 'mov', - args: [A, start] - }) - this.instructions.push({ - opcode: 'jnz', - args: [A] - }) - } else { - console.error(`!!! UNIMPLEMENTED NODE `, node.type, node) - } - } -} \ No newline at end of file diff --git a/the_e_programming_language/lang.ts b/the_e_programming_language/lang.ts deleted file mode 100755 index 5c00b98..0000000 --- a/the_e_programming_language/lang.ts +++ /dev/null @@ -1,31 +0,0 @@ -import Tokenizer from "./tokenizer.ts"; -import ASTGen from "./ast.ts"; -import Compiler from "./compiler.ts"; -const input = Deno.readTextFileSync('test.e') - -const tokenizer = new Tokenizer(input); -const tokens = tokenizer.tokenize(); - -console.log(tokens) - -const astGenerator = new ASTGen(tokens); - -let ast; -try { - ast = astGenerator.parse() -} catch (error) { - console.error(error); - console.log('at', astGenerator.position, tokens.map((a, i) => i == astGenerator.position ? `${a.type}(${a.value}) <--` : `${a.type}(${a.value})`).join('\n')) - Deno.exit(1) -} - -console.log(ast) - -const compiler = new Compiler(ast); - -for (const node of compiler.AST) { - compiler.compile(node) -} - -Deno.writeTextFileSync('ast.json', JSON.stringify(ast, null, 4)) -Deno.writeTextFileSync('code.txt', compiler.instructions.map(i => `${i.opcode}${i.args.length > 0 ? ' ' : ''}${i.args.join(',')}`).join('\n')) \ No newline at end of file diff --git a/the_e_programming_language/test-2.e b/the_e_programming_language/test-2.e deleted file mode 100755 index 3b1b371..0000000 --- a/the_e_programming_language/test-2.e +++ /dev/null @@ -1,6 +0,0 @@ -int[8] a = 0 -int test = 5 -fn _start { -a[4] = 5 -a[test] = 6 -} \ No newline at end of file diff --git a/the_e_programming_language/test.e b/the_e_programming_language/test.e deleted file mode 100755 index 4996d5c..0000000 --- a/the_e_programming_language/test.e +++ /dev/null @@ -1,43 +0,0 @@ -// comments exist -// -// types (in addresses (they store 2 bytes if you forgot)): -// int - 1 -// char - 1 -// bool - 1 -// uuh yeah,, theyre all 1 address ;-; -// -// $VARNAME is the address of the var, when used in mov it means that we get that var -// [$VARNAME] is the address of the var, but when it's used in mov it means we set to the address - -// translates to: -// mov a 0 -// mov $counter a -// prob not gonna make it do assembly tho -int[4] buff = 0 -int counter = 0 - -fn _start { - // translates to - // <inner code> - // mov a $counter - // mov b 4 - // cmp a b - // jz $start_of_inner_code - while (counter < 4) { - // translates to - // mov a [$buff] - // mov b [$counter] - // add a a b - // mov b $counter - // str a b - buff[counter] = counter - // translates to - // mov a $counter ; further translates to: - // ; mov a $counter - // ; ld a a - // mov b 1 - // add a a b - // str $counter a - counter = counter + 1 - } -} \ No newline at end of file diff --git a/the_e_programming_language/test.e.txt b/the_e_programming_language/test.e.txt deleted file mode 100755 index c8d09c5..0000000 --- a/the_e_programming_language/test.e.txt +++ /dev/null @@ -1,43 +0,0 @@ -// comments exist -// -// types (in addresses (they store 2 bytes if you forgot)): -// int - 1 -// char - 1 -// bool - 1 -// uuh yeah,, theyre all 1 address ;-; -// -// $VARNAME is the address of the var, when used in mov it means that we get that var -// [$VARNAME] is the address of the var, but when it's used in mov it means we set to the address - -// translates to: -// mov a 0 -// mov $counter a -// prob not gonna make it do assembly tho -int counter = 0 -int[4] buff = 0 - -fn _start { - // translates to - // <inner code> - // mov a $counter - // mov b 4 - // cmp a b - // jz $start_of_inner_code - while (counter < 4) { - // translates to - // mov a [$buff] - // mov b [$counter] - // add a a b - // mov b $counter - // str a b - buff[counter] = counter - // translates to - // mov a $counter ; further translates to: - // ; mov a $counter - // ; ld a a - // mov b 1 - // add a a b - // str $counter a - counter = counter + 1 - } -} \ No newline at end of file diff --git a/the_e_programming_language/tokenizer.ts b/the_e_programming_language/tokenizer.ts deleted file mode 100755 index 57d0131..0000000 --- a/the_e_programming_language/tokenizer.ts +++ /dev/null @@ -1,171 +0,0 @@ -export enum TokenType { - TYPE = "TYPE", - FN_DECL = "FN_DECL", - LITERAL = "LITERAL", - NUMBER = "NUMBER", - LPAREN = "LPAREN", - RPAREN = "RPAREN", - LBRACE = "LBRACE", - RBRACE = "RBRACE", - LBRACKET = "LBRACKER", - RBRACKET = "RBRACKER", - COMMA = "COMMA", - WHILE = "WHILE", - IF = "IF", - ELSE = "ELSE", - ASSIGN = "ASSIGN", - BINOP = "BINOP", - IDENTIFIER = "IDENTIFIER", - - EOF = "EOF", -} - -export interface Token { - type: TokenType; - value: string; -} - -const types = ['int', 'bool', 'char'] - -// i hardly know her -export default class Tokenizer { - private source: string; - private position: number = 0; - - constructor(source: string) { - this.source = source; - } - - private isAlpha(char: string): boolean { - return /[a-zA-Z_#]/.test(char); - } - - private isDigit(char: string): boolean { - return /-?[\d\.]/.test(char); - } - - private isWhitespace(char: string): boolean { - return /\s/.test(char); - } - - private advance(): string { - return this.source[this.position++]; - } - - private peek(): string { - return this.source[this.position] || ""; - } - - private match(expected: string): boolean { - if (this.peek() === expected) { - this.position++; - return true; - } - return false; - } - - tokenize(): Token[] { - const tokens: Token[] = []; - - let inComment = false; - let global = 0; - - while (this.position < this.source.length) { - const char = this.advance(); - - if (global > 0) global--; - - if (inComment) { - if (char == '\n') inComment = false; - continue; - } else if (char == '/' && this.peek() == '/') { - inComment = true - } else if (this.isWhitespace(char)) { - continue; - } else if (this.isAlpha(char)) { - let identifier = char; - while (this.isAlpha(this.peek()) || this.isDigit(this.peek())) { - identifier += this.advance(); - } - - // if (identifier === "#include") { - // // while(this.isWhitespace(this.peek()) && this.position < this.source.length) { - // // this.advance() - // // } - // // if (this.advance() != '<') throw "Expected a < after #include" - // // let id = '' - // // while (this.peek() != '>' - // // && (this.isAlpha(this.peek()) || this.isDigit(this.peek()))) { - // // id += this.advance(); - // // } - // // if (this.advance() != '>') throw "Expected a > after #include <..." - // tokens.push({ type: TokenType.INCLUDE, value: identifier }); - // } else if (identifier === "var") tokens.push({ type: TokenType.VAR, value: global > 0 ? 'global' : identifier }); - // else if (identifier === "list") tokens.push({ type: TokenType.LIST, value: global > 0 ? 'global' : identifier }); - // else if (identifier === "global") global = 2; - if (identifier === "fn") tokens.push({ type: TokenType.FN_DECL, value: identifier }); - else if (identifier === "if") tokens.push({ type: TokenType.IF, value: identifier }); - else if (identifier === "else") tokens.push({ type: TokenType.ELSE, value: identifier }); - else if (identifier === "while") tokens.push({ type: TokenType.WHILE, value: identifier }); - else if (types.includes(identifier)) tokens.push({ type: TokenType.TYPE, value: identifier }); - // else if (identifier === "for") tokens.push({ type: TokenType.FOR, value: identifier }); - else tokens.push({ type: TokenType.IDENTIFIER, value: identifier }); - } else if (this.isDigit(char)) { - let number = char; - while (this.isDigit(this.peek())) { - number += this.advance(); - } - tokens.push({ type: TokenType.NUMBER, value: number }); - } else if (char === '"') { - let string = ""; - while (this.peek() !== '"' && this.peek() !== "") { - string += this.advance(); - } - if (!this.match('"')) { - throw new Error("Unterminated string"); - } - tokens.push({ type: TokenType.LITERAL, value: string }); - } else if (char === "(") tokens.push({ type: TokenType.LPAREN, value: char }); - else if (char === ")") tokens.push({ type: TokenType.RPAREN, value: char }); - else if (char === "{") tokens.push({ type: TokenType.LBRACE, value: char }); - else if (char === "}") tokens.push({ type: TokenType.RBRACE, value: char }); - else if (char === "[") tokens.push({ type: TokenType.LBRACKET, value: char }); - else if (char === "]") tokens.push({ type: TokenType.RBRACKET, value: char }); - else if (char === ",") tokens.push({ type: TokenType.COMMA, value: char }); - else if (char === "+") tokens.push({ type: TokenType.BINOP, value: char }); - else if (char === "-") tokens.push({ type: TokenType.BINOP, value: char }); - else if (char === "*") tokens.push({ type: TokenType.BINOP, value: char }); - else if (char === "/") tokens.push({ type: TokenType.BINOP, value: char }); - else if (char === "%") tokens.push({ type: TokenType.BINOP, value: char }); - else if (char === "=" && this.peek() === '=') { - tokens.push({ type: TokenType.BINOP, value: char }); - this.advance(); - } - else if (char === "&" && this.peek() === '&') { - tokens.push({ type: TokenType.BINOP, value: char }); - this.advance(); - } - else if (char === "!" && this.peek() === '=') { - tokens.push({ type: TokenType.BINOP, value: '!=' }); - this.advance(); - } - else if (char === "<" && this.peek() === '=') { - tokens.push({ type: TokenType.BINOP, value: '<=' }); - this.advance(); - } - else if (char === ">" && this.peek() === '=') { - tokens.push({ type: TokenType.BINOP, value: '>=' }); - this.advance(); - } - else if (char === ">") tokens.push({ type: TokenType.BINOP, value: char }); - else if (char === "<") tokens.push({ type: TokenType.BINOP, value: char }); - else if (char === "=") tokens.push({ type: TokenType.ASSIGN, value: char }); - else { - throw new Error(`Unexpected character: ${char}`); - } - } - - tokens.push({ type: TokenType.EOF, value: "" }); - return tokens; - } -} \ No newline at end of file |