From 7d938ee07a83daa8b1e254a7ab8a46d32d75dbfe Mon Sep 17 00:00:00 2001 From: Luiz <40842327+MyNameIsBatman@users.noreply.github.com> Date: Tue, 24 May 2022 20:58:33 -0300 Subject: [PATCH 1/7] Updates --- "docs/Gram\303\241tica.txt" | 3 + src/_examples/SomaDoisNumeros.txt | 16 ++ src/_examples/teste.txt | 9 + src/index.ts | 6 +- src/lexer/Lexer.ts | 251 ++++++++++++++++++++++++ src/lexer/LexerError.ts | 22 +++ src/lexer/Token.ts | 74 +++++++ src/parser/Parser.ts | 50 +++++ src/parser/nodes/BinaryOperationNode.ts | 14 ++ src/parser/nodes/Node.ts | 16 ++ src/parser/nodes/NumberNode.ts | 6 + 11 files changed, 464 insertions(+), 3 deletions(-) create mode 100644 "docs/Gram\303\241tica.txt" create mode 100644 src/_examples/SomaDoisNumeros.txt create mode 100644 src/_examples/teste.txt create mode 100644 src/lexer/Lexer.ts create mode 100644 src/lexer/LexerError.ts create mode 100644 src/lexer/Token.ts create mode 100644 src/parser/Parser.ts create mode 100644 src/parser/nodes/BinaryOperationNode.ts create mode 100644 src/parser/nodes/Node.ts create mode 100644 src/parser/nodes/NumberNode.ts diff --git "a/docs/Gram\303\241tica.txt" "b/docs/Gram\303\241tica.txt" new file mode 100644 index 0000000..3652f65 --- /dev/null +++ "b/docs/Gram\303\241tica.txt" @@ -0,0 +1,3 @@ +Expressão : termo ((MAIS/MENOS) termo)* +Termo : fator ((MULTIPLICAR/DIVIDIR) fator)* +Fator : INT|FLOAT \ No newline at end of file diff --git a/src/_examples/SomaDoisNumeros.txt b/src/_examples/SomaDoisNumeros.txt new file mode 100644 index 0000000..7212efe --- /dev/null +++ b/src/_examples/SomaDoisNumeros.txt @@ -0,0 +1,16 @@ +var valor1 = 0; +var valor2 = 0; + +print("Digite o valor 1 de um número inteiro"); +read(valor1); + +print("Digite o valor 2 de um número inteiro"); +read(valor2); + +//Processamento do programa +const resultado = valor1 + valor2; + +if(resultado >= 10) + print("O valor é maior ou igual a 10. O resultado é " + resultado); +else + print("O valor é menor que 10. O resultado é " + resultado); \ No newline at end of file diff --git a/src/_examples/teste.txt b/src/_examples/teste.txt new file mode 100644 index 0000000..6aca5c7 --- /dev/null +++ b/src/_examples/teste.txt @@ -0,0 +1,9 @@ +{ + //Primeiro comentário awuiiiii + const abc= 1345.65;var vfd="meu email é 1800935@escolas.anchieta.br"; + if(abc === 1) abc++; + else abc--; + const finalSum = abc*2; + + //Segundo comentário aqui!!!! +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index a6146ce..83a9dba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ -import { Lexer } from "./models/Lexer"; -import { Token } from "./models/Token"; +import { Lexer } from "./lexer/Lexer"; +import { Token } from "./lexer/Token"; import fs from 'fs'; import path from 'path'; -const FILENAME: string = 'examples/SomaDoisNumeros.txt'; +const FILENAME: string = '_examples/SomaDoisNumeros.txt'; const filePath: string = path.join(__dirname, FILENAME); diff --git a/src/lexer/Lexer.ts b/src/lexer/Lexer.ts new file mode 100644 index 0000000..10e2214 --- /dev/null +++ b/src/lexer/Lexer.ts @@ -0,0 +1,251 @@ +import { LexerError, LexerErrors } from "./LexerError"; +import { KeywordType, SymbolType, Token, TokenType } from "./Token"; + +const STARTING_DIGITS: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; +const DIGITS: string[] = [...STARTING_DIGITS, '.']; + +const STARTING_CHARS: string[] = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']; +const CHARS: string[] = [...STARTING_CHARS, ...STARTING_DIGITS, '_']; + +const STRING_INIT_SYMBOL: string = '"'; +const STRING_STOP_SYMBOL: string = '"'; +const COMMENT_INIT_SYMBOL: string = '//'; +const COMMENT_STOP_SYMBOL: string = '\n'; + +const SYMBOLS: string[] = ['\'', '*', '-', '+', '/', ';', '{', '}', '(', ')', '=', '|', '&', '%', '!', '<', '>']; +const COMPOUND_SYMBOLS: string[] = [ '--', '++', '==', '===', '!=', '<=', '>=', '||', '&&']; + +const KEYWORDS: string[] = ['break','return','continue','for','while', 'var', 'const', 'if', 'else', 'elseif', 'print', 'read']; +const IGNORED_TOKENS: string[] = ['\n', '\t', '\r', ' ']; + +export class Lexer +{ + text: string; + currentPosition: number; + + constructor(text: string) + { + this.text = text; + this.currentPosition = -1; + } + + private get currentChar(): string | null + { + return this.text[this.currentPosition] || null + } + + private advance(): void + { + this.currentPosition++; + } + + public findTokens(): Token[] + { + const tokens: Token[] = []; + + this.advance(); + while(this.currentChar) + { + if(COMMENT_INIT_SYMBOL.startsWith(this.currentChar)) + { + this.makeComment(); + continue; + } + + if(IGNORED_TOKENS.includes(this.currentChar)) + { + this.advance(); + continue; + } + + if(STRING_INIT_SYMBOL === this.currentChar) + { + tokens.push(this.makeString()); + continue; + } + + if(STARTING_CHARS.includes(this.currentChar)) + { + tokens.push(this.makeIdentifier()); + continue; + } + + if(SYMBOLS.includes(this.currentChar)) + { + tokens.push(...this.makeSymbol()); + continue; + } + + if(STARTING_DIGITS.includes(this.currentChar)) + { + tokens.push(this.makeNumber()); + continue; + } + + this.error(LexerErrors.ILLEGAL_CHAR, `${this.currentChar} at position ${this.currentPosition}`); + } + + return tokens; + } + + private makeNumber(): Token + { + let numberStr: string = ''; + let dotCount: number = 0; + + while(this.currentChar && DIGITS.includes(this.currentChar)) + { + if(this.currentChar === '.') + { + if(dotCount > 0) break; + + dotCount++; + } + + numberStr = numberStr + this.currentChar; + this.advance(); + } + + if(dotCount === 0) return new Token(TokenType.INTEGER, parseInt(numberStr)); + + return new Token(TokenType.FLOAT, parseFloat(numberStr)) + } + + private makeComment(): void + { + this.advance(); + + while(this.currentChar) + { + if(this.currentChar === COMMENT_STOP_SYMBOL) break; + + this.advance(); + } + + if(this.currentChar === COMMENT_STOP_SYMBOL) this.advance(); + } + + private makeIdentifier(): Token + { + let str: string = ''; + + while(this.currentChar && CHARS.includes(this.currentChar.toLowerCase())) + { + str = str + this.currentChar; + this.advance(); + } + + if(KEYWORDS.includes(str)) return new Token(TokenType.KEYWORD, str, this.getKeywordType(str)); + + return new Token(TokenType.IDENTIFIER, str); + } + + private makeString(): Token + { + let str: string = ''; + let closedStr: boolean = false; + + this.advance(); + + while(this.currentChar) + { + if(this.currentChar === STRING_STOP_SYMBOL) + { + closedStr = true; + break; + } + + str = str + this.currentChar; + this.advance(); + } + + if(!closedStr) this.error(LexerErrors.NOT_CLOSED_STRING, `At position ${this.currentPosition}`); + + if(this.currentChar === STRING_STOP_SYMBOL) this.advance(); + + return new Token(TokenType.STRING, str); + } + + private makeSymbol(): Token[] + { + let str: string = ''; + + while(this.currentChar && SYMBOLS.includes(this.currentChar)) + { + const filter: string[] = COMPOUND_SYMBOLS.filter(s => s.startsWith(str)); + + if(filter.length === 1 && filter[0] === str) break; + + str = str + this.currentChar; + + this.advance(); + } + + if(COMPOUND_SYMBOLS.includes(str)) + return [new Token(TokenType.COMPOUND_SYMBOL, str, this.getSymbolSubType(str))]; + + const tokens: Token[] = []; + + for(const symbol of str.split('')) + tokens.push(new Token(TokenType.SYMBOL, symbol, this.getSymbolSubType(symbol))); + + return tokens; + } + + private getSymbolSubType(symbol: string): string | null + { + switch(symbol) + { + case '-': return SymbolType.MINUS; + case '+': return SymbolType.PLUS; + case '*': return SymbolType.MULTIPLY; + case '/': return SymbolType.DIVIDE; + case ';': return SymbolType.SEMICOLON; + case '{': return SymbolType.LEFT_BRACE; + case '}': return SymbolType.RIGHT_BRACE; + case '(': return SymbolType.LEFT_PARENTHESES; + case ')': return SymbolType.RIGHT_PARENTHESES; + case '"': return SymbolType.DOUBLE_QUOTES; + case '=': return SymbolType.ATTRIBUTION; + case '|': return SymbolType.VERTICAL_LINE; + case '&': return SymbolType.AMPERSAND; + case '%': return SymbolType.PERCENTAGE; + case '!': return SymbolType.EXCLAMATION; + + case '==': return SymbolType.EQUALS; + case '===': return SymbolType.EXACTLY_EQUALS; + case '!=': return SymbolType.NOT_EQUAL; + case '<=': return SymbolType.LESS_OR_EQUALS_TO; + case '>=': return SymbolType.MORE_OR_EQUALS_TO; + case '||': return SymbolType.OR; + case '&&': return SymbolType.AND; + case '++': return SymbolType.INCREMENT; + case '--': return SymbolType.DECREMENT; + + default: return null; + } + } + + private getKeywordType(keyword: string): string | null + { + switch(keyword) + { + case 'break': return KeywordType.BREAK; + case 'return': return KeywordType.RETURN; + case 'continue': return KeywordType.CONTINUE; + case 'for': return KeywordType.FOR; + case 'while': return KeywordType.WHILE; + case 'var': return KeywordType.VAR; + case 'const': return KeywordType.CONST; + case 'if': return KeywordType.IF; + case 'else': return KeywordType.ELSE; + case 'elseif': return KeywordType.ELSEIF; + default: return null; + } + } + + private error(type: string, message: string): void + { + throw new LexerError(type, message); + } +} \ No newline at end of file diff --git a/src/lexer/LexerError.ts b/src/lexer/LexerError.ts new file mode 100644 index 0000000..593d3ca --- /dev/null +++ b/src/lexer/LexerError.ts @@ -0,0 +1,22 @@ +export class LexerError +{ + name: string; + details: string; + + constructor(name: string, details: string) + { + this.name = name; + this.details = details; + } + + public asString(): string + { + return ` ${this.name}: ${this.details}`; + } +} + +export class LexerErrors +{ + public static readonly ILLEGAL_CHAR: string = 'ILLEGAL_CHAR'; + public static readonly NOT_CLOSED_STRING: string = 'NOT_CLOSED_STRING'; +} \ No newline at end of file diff --git a/src/lexer/Token.ts b/src/lexer/Token.ts new file mode 100644 index 0000000..5325e43 --- /dev/null +++ b/src/lexer/Token.ts @@ -0,0 +1,74 @@ +export class Token +{ + value: any; + type: string; + subType?: string | null; + + constructor(type: string, value: any, subType?: string | null) + { + this.value = value; + this.type = type; + this.subType = subType || null; + } + + public get deepType(): string + { + return this.subType ? this.subType : this.type; + } +} + +export class TokenType +{ + public static readonly SYMBOL: string = 'SYMBOL'; + public static readonly COMPOUND_SYMBOL: string = 'COMPOUND_SYMBOL'; + public static readonly KEYWORD: string = 'KEYWORD'; + public static readonly INTEGER: string = 'INTEGER'; + public static readonly FLOAT: string = 'FLOAT'; + public static readonly STRING: string = 'STRING'; + public static readonly IDENTIFIER: string = 'IDENTIFIER'; +} + +export class SymbolType +{ + public static readonly MINUS: string = 'SYMBOL_MINUS'; + public static readonly PLUS: string = 'SYMBOL_PLUS'; + public static readonly MULTIPLY: string = 'SYMBOL_MULTIPLY'; + public static readonly DIVIDE: string = 'SYMBOL_DIVIDE'; + public static readonly SEMICOLON: string = 'SYMBOL_SEMICOLON'; + public static readonly LEFT_BRACE: string = 'SYMBOL_LEFT_BRACE'; + public static readonly RIGHT_BRACE: string = 'SYMBOL_RIGHT_BRACE'; + public static readonly LEFT_PARENTHESES: string = 'SYMBOL_LEFT_PARENTHESES'; + public static readonly RIGHT_PARENTHESES: string = 'SYMBOL_RIGHT_PARENTHESES'; + public static readonly DOUBLE_QUOTES: string = 'SYMBOL_DOUBLE_QUOTES'; + public static readonly ATTRIBUTION: string = 'SYMBOL_ATTRIBUTION'; + public static readonly VERTICAL_LINE: string = 'SYMBOL_VERTICAL_LINE'; + public static readonly AMPERSAND: string = 'SYMBOL_AMPERSAND'; + public static readonly PERCENTAGE: string = 'SYMBOL_PERCENTAGE'; + public static readonly EXCLAMATION: string = 'SYMBOL_EXCLAMATION'; + + public static readonly EQUALS: string = 'SYMBOL_EQUALS'; + public static readonly EXACTLY_EQUALS: string = 'SYMBOL_EXACTLY_EQUALS'; + public static readonly NOT_EQUAL: string = 'SYMBOL_NOT_EQUAL'; + public static readonly LESS_OR_EQUALS_TO: string = 'SYMBOL_LESS_OR_EQUALS_TO'; + public static readonly MORE_OR_EQUALS_TO: string = 'SYMBOL_MORE_OR_EQUALS_TO'; + public static readonly OR: string = 'SYMBOL_OR'; + public static readonly AND: string = 'SYMBOL_AND'; + public static readonly INCREMENT: string = 'SYMBOL_INCREMENT'; + public static readonly DECREMENT: string = 'SYMBOL_DECREMENT'; +} + +export class KeywordType +{ + public static readonly BREAK: string = 'KEYWORD_BREAK'; + public static readonly RETURN: string = 'KEYWORD_RETURN'; + public static readonly CONTINUE: string = 'KEYWORD_CONTINUE'; + public static readonly FOR: string = 'KEYWORD_FOR'; + public static readonly WHILE: string = 'KEYWORD_WHILE'; + public static readonly VAR: string = 'KEYWORD_VAR'; + public static readonly CONST: string = 'KEYWORD_CONST'; + public static readonly IF: string = 'KEYWORD_IF'; + public static readonly ELSE: string = 'KEYWORD_ELSE'; + public static readonly ELSEIF: string = 'KEYWORD_ELSEIF'; + public static readonly PRINT: string = 'KEYWORD_PRINT'; + public static readonly READ: string = 'KEYWORD_READ'; +} \ No newline at end of file diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts new file mode 100644 index 0000000..23564f5 --- /dev/null +++ b/src/parser/Parser.ts @@ -0,0 +1,50 @@ +import { SymbolType, Token, TokenType } from "../lexer/Token"; +import { NumberNode } from "./nodes/NumberNode"; + +const FACTORS: string[] = [TokenType.INTEGER, TokenType.FLOAT]; +const TERMS: string[] = [SymbolType.MULTIPLY, SymbolType.DIVIDE]; + +export class Parser +{ + tokens: Token[]; + currentPosition: number; + + constructor(tokens: Token[]) + { + this.tokens = tokens; + this.currentPosition = -1; + } + + private get currentToken(): Token | null + { + return this.tokens[this.currentPosition] || null + } + + private advance(): void + { + this.currentPosition++; + } + + public buildTree(): void + { + this.advance(); + } + + private makeFactor(): NumberNode | null + { + if(!FACTORS.includes(this.currentToken.deepType)) return null; + + this.advance(); + return new NumberNode(this.currentToken); + } + + private makeTerm(): void + { + const leftFactor: NumberNode | null = this.makeFactor(); + + while(this.currentToken && TERMS.includes(this.currentToken.deepType)) + { + //const operator: + } + } +} \ No newline at end of file diff --git a/src/parser/nodes/BinaryOperationNode.ts b/src/parser/nodes/BinaryOperationNode.ts new file mode 100644 index 0000000..ad692b8 --- /dev/null +++ b/src/parser/nodes/BinaryOperationNode.ts @@ -0,0 +1,14 @@ +import { Token } from "../../lexer/Token"; +import { ParserNode } from "./Node"; + +export class BinaryOperationNode +{ + leftNode: ParserNode; + operationToken: Token; + rightNode: ParserNode; + + public toString(): string + { + return `(${this.leftNode.toString()}, ${this.operationToken.value}, ${this.rightNode.toString()})`; + } +} \ No newline at end of file diff --git a/src/parser/nodes/Node.ts b/src/parser/nodes/Node.ts new file mode 100644 index 0000000..051cc5f --- /dev/null +++ b/src/parser/nodes/Node.ts @@ -0,0 +1,16 @@ +import { Token } from "../../lexer/Token"; + +export class ParserNode +{ + token: Token; + + constructor(token: Token) + { + this.token = token; + } + + public toString(): string + { + return this.token.value; + } +} \ No newline at end of file diff --git a/src/parser/nodes/NumberNode.ts b/src/parser/nodes/NumberNode.ts new file mode 100644 index 0000000..873308e --- /dev/null +++ b/src/parser/nodes/NumberNode.ts @@ -0,0 +1,6 @@ +import { ParserNode } from "./Node"; + +export class NumberNode extends ParserNode +{ + +} \ No newline at end of file From 98120a7411b2cdd9d7e5753fc7e8553df12cc7a7 Mon Sep 17 00:00:00 2001 From: MyNameIsBatman Date: Thu, 26 May 2022 20:58:34 -0300 Subject: [PATCH 2/7] Updates --- "docs/Gram\303\241tica.txt" | 15 +- src/_examples/somaso.txt | 1 + src/examples/SomaDoisNumeros.txt | 16 -- src/examples/teste.txt | 9 - src/index.ts | 6 +- src/lexer/Lexer.ts | 5 +- src/lexer/LexerError.ts | 2 +- src/lexer/Token.ts | 2 + src/models/Lexer.ts | 251 -------------------- src/models/LexerError.ts | 22 -- src/models/Token.ts | 69 ------ src/parser/Parser.ts | 149 +++++++++++- src/parser/ParserError.ts | 21 ++ src/parser/ParserResult.ts | 13 + src/parser/nodes/BinaryOperationNode.ts | 16 +- src/parser/nodes/Node.ts | 16 -- src/parser/nodes/NumberNode.ts | 14 +- src/parser/nodes/ParserNode.ts | 7 + src/parser/nodes/UnaryOperationNode.ts | 20 ++ src/parser/nodes/VariableAccessNode.ts | 18 ++ src/parser/nodes/VariableAssignementNode.ts | 20 ++ 21 files changed, 288 insertions(+), 404 deletions(-) create mode 100644 src/_examples/somaso.txt delete mode 100644 src/examples/SomaDoisNumeros.txt delete mode 100644 src/examples/teste.txt delete mode 100644 src/models/Lexer.ts delete mode 100644 src/models/LexerError.ts delete mode 100644 src/models/Token.ts create mode 100644 src/parser/ParserError.ts create mode 100644 src/parser/ParserResult.ts delete mode 100644 src/parser/nodes/Node.ts create mode 100644 src/parser/nodes/ParserNode.ts create mode 100644 src/parser/nodes/UnaryOperationNode.ts create mode 100644 src/parser/nodes/VariableAccessNode.ts create mode 100644 src/parser/nodes/VariableAssignementNode.ts diff --git "a/docs/Gram\303\241tica.txt" "b/docs/Gram\303\241tica.txt" index 3652f65..9e2b814 100644 --- "a/docs/Gram\303\241tica.txt" +++ "b/docs/Gram\303\241tica.txt" @@ -1,3 +1,12 @@ -Expressão : termo ((MAIS/MENOS) termo)* -Termo : fator ((MULTIPLICAR/DIVIDIR) fator)* -Fator : INT|FLOAT \ No newline at end of file +expressao : VAR|CONST IDENTIFIER EQ expressao + : termo ((MAIS|MENOS) termo)* + +termo : fator ((MULTIPLICAR|DIVIDIR) fator)* + +fator : (MAIS|MENOS) fator + : power + +power : atom (POWER fator)* + +atom : INT|FLOAT|IDENTIFIER + : LEFT_PARETENSYS fator RIGHT_PARENTESYS \ No newline at end of file diff --git a/src/_examples/somaso.txt b/src/_examples/somaso.txt new file mode 100644 index 0000000..5d4b038 --- /dev/null +++ b/src/_examples/somaso.txt @@ -0,0 +1 @@ +vars resultado = (1+2)*3+4+abc+6+7+8^2 \ No newline at end of file diff --git a/src/examples/SomaDoisNumeros.txt b/src/examples/SomaDoisNumeros.txt deleted file mode 100644 index 7212efe..0000000 --- a/src/examples/SomaDoisNumeros.txt +++ /dev/null @@ -1,16 +0,0 @@ -var valor1 = 0; -var valor2 = 0; - -print("Digite o valor 1 de um número inteiro"); -read(valor1); - -print("Digite o valor 2 de um número inteiro"); -read(valor2); - -//Processamento do programa -const resultado = valor1 + valor2; - -if(resultado >= 10) - print("O valor é maior ou igual a 10. O resultado é " + resultado); -else - print("O valor é menor que 10. O resultado é " + resultado); \ No newline at end of file diff --git a/src/examples/teste.txt b/src/examples/teste.txt deleted file mode 100644 index 6aca5c7..0000000 --- a/src/examples/teste.txt +++ /dev/null @@ -1,9 +0,0 @@ -{ - //Primeiro comentário awuiiiii - const abc= 1345.65;var vfd="meu email é 1800935@escolas.anchieta.br"; - if(abc === 1) abc++; - else abc--; - const finalSum = abc*2; - - //Segundo comentário aqui!!!! -} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 83a9dba..05f1132 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,8 +2,9 @@ import { Lexer } from "./lexer/Lexer"; import { Token } from "./lexer/Token"; import fs from 'fs'; import path from 'path'; +import { Parser } from "./parser/Parser"; -const FILENAME: string = '_examples/SomaDoisNumeros.txt'; +const FILENAME: string = '_examples/somaso.txt'; const filePath: string = path.join(__dirname, FILENAME); @@ -13,6 +14,9 @@ fs.readFile(filePath, {encoding: 'utf-8'}, (err, data) => { const tokens: Token[] = lexer.findTokens(); console.table(tokens); + + const parser: Parser = new Parser(tokens); + parser.buildTree(); } else { console.error(err); } diff --git a/src/lexer/Lexer.ts b/src/lexer/Lexer.ts index 10e2214..8a51dcc 100644 --- a/src/lexer/Lexer.ts +++ b/src/lexer/Lexer.ts @@ -12,7 +12,7 @@ const STRING_STOP_SYMBOL: string = '"'; const COMMENT_INIT_SYMBOL: string = '//'; const COMMENT_STOP_SYMBOL: string = '\n'; -const SYMBOLS: string[] = ['\'', '*', '-', '+', '/', ';', '{', '}', '(', ')', '=', '|', '&', '%', '!', '<', '>']; +const SYMBOLS: string[] = ['\'', '*', '-', '+', '/', ';', '{', '}', '(', ')', '=', '|', '&', '%', '!', '<', '>', '^']; const COMPOUND_SYMBOLS: string[] = [ '--', '++', '==', '===', '!=', '<=', '>=', '||', '&&']; const KEYWORDS: string[] = ['break','return','continue','for','while', 'var', 'const', 'if', 'else', 'elseif', 'print', 'read']; @@ -85,6 +85,8 @@ export class Lexer this.error(LexerErrors.ILLEGAL_CHAR, `${this.currentChar} at position ${this.currentPosition}`); } + tokens.push(new Token(TokenType.END_OF_FILE, null)); + return tokens; } @@ -211,6 +213,7 @@ export class Lexer case '&': return SymbolType.AMPERSAND; case '%': return SymbolType.PERCENTAGE; case '!': return SymbolType.EXCLAMATION; + case '^': return SymbolType.POWER; case '==': return SymbolType.EQUALS; case '===': return SymbolType.EXACTLY_EQUALS; diff --git a/src/lexer/LexerError.ts b/src/lexer/LexerError.ts index 593d3ca..9113b9c 100644 --- a/src/lexer/LexerError.ts +++ b/src/lexer/LexerError.ts @@ -11,7 +11,7 @@ export class LexerError public asString(): string { - return ` ${this.name}: ${this.details}`; + return `[LEXER] ${this.name}: ${this.details}`; } } diff --git a/src/lexer/Token.ts b/src/lexer/Token.ts index 5325e43..4d6b297 100644 --- a/src/lexer/Token.ts +++ b/src/lexer/Token.ts @@ -26,6 +26,7 @@ export class TokenType public static readonly FLOAT: string = 'FLOAT'; public static readonly STRING: string = 'STRING'; public static readonly IDENTIFIER: string = 'IDENTIFIER'; + public static readonly END_OF_FILE: string = 'EOF'; } export class SymbolType @@ -45,6 +46,7 @@ export class SymbolType public static readonly AMPERSAND: string = 'SYMBOL_AMPERSAND'; public static readonly PERCENTAGE: string = 'SYMBOL_PERCENTAGE'; public static readonly EXCLAMATION: string = 'SYMBOL_EXCLAMATION'; + public static readonly POWER: string = 'SYMBOL_POWER'; public static readonly EQUALS: string = 'SYMBOL_EQUALS'; public static readonly EXACTLY_EQUALS: string = 'SYMBOL_EXACTLY_EQUALS'; diff --git a/src/models/Lexer.ts b/src/models/Lexer.ts deleted file mode 100644 index 10e2214..0000000 --- a/src/models/Lexer.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { LexerError, LexerErrors } from "./LexerError"; -import { KeywordType, SymbolType, Token, TokenType } from "./Token"; - -const STARTING_DIGITS: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; -const DIGITS: string[] = [...STARTING_DIGITS, '.']; - -const STARTING_CHARS: string[] = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']; -const CHARS: string[] = [...STARTING_CHARS, ...STARTING_DIGITS, '_']; - -const STRING_INIT_SYMBOL: string = '"'; -const STRING_STOP_SYMBOL: string = '"'; -const COMMENT_INIT_SYMBOL: string = '//'; -const COMMENT_STOP_SYMBOL: string = '\n'; - -const SYMBOLS: string[] = ['\'', '*', '-', '+', '/', ';', '{', '}', '(', ')', '=', '|', '&', '%', '!', '<', '>']; -const COMPOUND_SYMBOLS: string[] = [ '--', '++', '==', '===', '!=', '<=', '>=', '||', '&&']; - -const KEYWORDS: string[] = ['break','return','continue','for','while', 'var', 'const', 'if', 'else', 'elseif', 'print', 'read']; -const IGNORED_TOKENS: string[] = ['\n', '\t', '\r', ' ']; - -export class Lexer -{ - text: string; - currentPosition: number; - - constructor(text: string) - { - this.text = text; - this.currentPosition = -1; - } - - private get currentChar(): string | null - { - return this.text[this.currentPosition] || null - } - - private advance(): void - { - this.currentPosition++; - } - - public findTokens(): Token[] - { - const tokens: Token[] = []; - - this.advance(); - while(this.currentChar) - { - if(COMMENT_INIT_SYMBOL.startsWith(this.currentChar)) - { - this.makeComment(); - continue; - } - - if(IGNORED_TOKENS.includes(this.currentChar)) - { - this.advance(); - continue; - } - - if(STRING_INIT_SYMBOL === this.currentChar) - { - tokens.push(this.makeString()); - continue; - } - - if(STARTING_CHARS.includes(this.currentChar)) - { - tokens.push(this.makeIdentifier()); - continue; - } - - if(SYMBOLS.includes(this.currentChar)) - { - tokens.push(...this.makeSymbol()); - continue; - } - - if(STARTING_DIGITS.includes(this.currentChar)) - { - tokens.push(this.makeNumber()); - continue; - } - - this.error(LexerErrors.ILLEGAL_CHAR, `${this.currentChar} at position ${this.currentPosition}`); - } - - return tokens; - } - - private makeNumber(): Token - { - let numberStr: string = ''; - let dotCount: number = 0; - - while(this.currentChar && DIGITS.includes(this.currentChar)) - { - if(this.currentChar === '.') - { - if(dotCount > 0) break; - - dotCount++; - } - - numberStr = numberStr + this.currentChar; - this.advance(); - } - - if(dotCount === 0) return new Token(TokenType.INTEGER, parseInt(numberStr)); - - return new Token(TokenType.FLOAT, parseFloat(numberStr)) - } - - private makeComment(): void - { - this.advance(); - - while(this.currentChar) - { - if(this.currentChar === COMMENT_STOP_SYMBOL) break; - - this.advance(); - } - - if(this.currentChar === COMMENT_STOP_SYMBOL) this.advance(); - } - - private makeIdentifier(): Token - { - let str: string = ''; - - while(this.currentChar && CHARS.includes(this.currentChar.toLowerCase())) - { - str = str + this.currentChar; - this.advance(); - } - - if(KEYWORDS.includes(str)) return new Token(TokenType.KEYWORD, str, this.getKeywordType(str)); - - return new Token(TokenType.IDENTIFIER, str); - } - - private makeString(): Token - { - let str: string = ''; - let closedStr: boolean = false; - - this.advance(); - - while(this.currentChar) - { - if(this.currentChar === STRING_STOP_SYMBOL) - { - closedStr = true; - break; - } - - str = str + this.currentChar; - this.advance(); - } - - if(!closedStr) this.error(LexerErrors.NOT_CLOSED_STRING, `At position ${this.currentPosition}`); - - if(this.currentChar === STRING_STOP_SYMBOL) this.advance(); - - return new Token(TokenType.STRING, str); - } - - private makeSymbol(): Token[] - { - let str: string = ''; - - while(this.currentChar && SYMBOLS.includes(this.currentChar)) - { - const filter: string[] = COMPOUND_SYMBOLS.filter(s => s.startsWith(str)); - - if(filter.length === 1 && filter[0] === str) break; - - str = str + this.currentChar; - - this.advance(); - } - - if(COMPOUND_SYMBOLS.includes(str)) - return [new Token(TokenType.COMPOUND_SYMBOL, str, this.getSymbolSubType(str))]; - - const tokens: Token[] = []; - - for(const symbol of str.split('')) - tokens.push(new Token(TokenType.SYMBOL, symbol, this.getSymbolSubType(symbol))); - - return tokens; - } - - private getSymbolSubType(symbol: string): string | null - { - switch(symbol) - { - case '-': return SymbolType.MINUS; - case '+': return SymbolType.PLUS; - case '*': return SymbolType.MULTIPLY; - case '/': return SymbolType.DIVIDE; - case ';': return SymbolType.SEMICOLON; - case '{': return SymbolType.LEFT_BRACE; - case '}': return SymbolType.RIGHT_BRACE; - case '(': return SymbolType.LEFT_PARENTHESES; - case ')': return SymbolType.RIGHT_PARENTHESES; - case '"': return SymbolType.DOUBLE_QUOTES; - case '=': return SymbolType.ATTRIBUTION; - case '|': return SymbolType.VERTICAL_LINE; - case '&': return SymbolType.AMPERSAND; - case '%': return SymbolType.PERCENTAGE; - case '!': return SymbolType.EXCLAMATION; - - case '==': return SymbolType.EQUALS; - case '===': return SymbolType.EXACTLY_EQUALS; - case '!=': return SymbolType.NOT_EQUAL; - case '<=': return SymbolType.LESS_OR_EQUALS_TO; - case '>=': return SymbolType.MORE_OR_EQUALS_TO; - case '||': return SymbolType.OR; - case '&&': return SymbolType.AND; - case '++': return SymbolType.INCREMENT; - case '--': return SymbolType.DECREMENT; - - default: return null; - } - } - - private getKeywordType(keyword: string): string | null - { - switch(keyword) - { - case 'break': return KeywordType.BREAK; - case 'return': return KeywordType.RETURN; - case 'continue': return KeywordType.CONTINUE; - case 'for': return KeywordType.FOR; - case 'while': return KeywordType.WHILE; - case 'var': return KeywordType.VAR; - case 'const': return KeywordType.CONST; - case 'if': return KeywordType.IF; - case 'else': return KeywordType.ELSE; - case 'elseif': return KeywordType.ELSEIF; - default: return null; - } - } - - private error(type: string, message: string): void - { - throw new LexerError(type, message); - } -} \ No newline at end of file diff --git a/src/models/LexerError.ts b/src/models/LexerError.ts deleted file mode 100644 index 593d3ca..0000000 --- a/src/models/LexerError.ts +++ /dev/null @@ -1,22 +0,0 @@ -export class LexerError -{ - name: string; - details: string; - - constructor(name: string, details: string) - { - this.name = name; - this.details = details; - } - - public asString(): string - { - return ` ${this.name}: ${this.details}`; - } -} - -export class LexerErrors -{ - public static readonly ILLEGAL_CHAR: string = 'ILLEGAL_CHAR'; - public static readonly NOT_CLOSED_STRING: string = 'NOT_CLOSED_STRING'; -} \ No newline at end of file diff --git a/src/models/Token.ts b/src/models/Token.ts deleted file mode 100644 index f88641e..0000000 --- a/src/models/Token.ts +++ /dev/null @@ -1,69 +0,0 @@ -export class Token -{ - value: any; - type: string; - subType?: string | null; - - constructor(type: string, value: any, subType?: string | null) - { - this.value = value; - this.type = type; - this.subType = subType || null; - } -} - -export class TokenType -{ - public static readonly SYMBOL: string = 'SYMBOL'; - public static readonly COMPOUND_SYMBOL: string = 'COMPOUND_SYMBOL'; - public static readonly KEYWORD: string = 'KEYWORD'; - public static readonly INTEGER: string = 'INTEGER'; - public static readonly FLOAT: string = 'FLOAT'; - public static readonly STRING: string = 'STRING'; - public static readonly IDENTIFIER: string = 'IDENTIFIER'; -} - -export class SymbolType -{ - public static readonly MINUS: string = 'SYMBOL_MINUS'; - public static readonly PLUS: string = 'SYMBOL_PLUS'; - public static readonly MULTIPLY: string = 'SYMBOL_MULTIPLY'; - public static readonly DIVIDE: string = 'SYMBOL_DIVIDE'; - public static readonly SEMICOLON: string = 'SYMBOL_SEMICOLON'; - public static readonly LEFT_BRACE: string = 'SYMBOL_LEFT_BRACE'; - public static readonly RIGHT_BRACE: string = 'SYMBOL_RIGHT_BRACE'; - public static readonly LEFT_PARENTHESES: string = 'SYMBOL_LEFT_PARENTHESES'; - public static readonly RIGHT_PARENTHESES: string = 'SYMBOL_RIGHT_PARENTHESES'; - public static readonly DOUBLE_QUOTES: string = 'SYMBOL_DOUBLE_QUOTES'; - public static readonly ATTRIBUTION: string = 'SYMBOL_ATTRIBUTION'; - public static readonly VERTICAL_LINE: string = 'SYMBOL_VERTICAL_LINE'; - public static readonly AMPERSAND: string = 'SYMBOL_AMPERSAND'; - public static readonly PERCENTAGE: string = 'SYMBOL_PERCENTAGE'; - public static readonly EXCLAMATION: string = 'SYMBOL_EXCLAMATION'; - - public static readonly EQUALS: string = 'SYMBOL_EQUALS'; - public static readonly EXACTLY_EQUALS: string = 'SYMBOL_EXACTLY_EQUALS'; - public static readonly NOT_EQUAL: string = 'SYMBOL_NOT_EQUAL'; - public static readonly LESS_OR_EQUALS_TO: string = 'SYMBOL_LESS_OR_EQUALS_TO'; - public static readonly MORE_OR_EQUALS_TO: string = 'SYMBOL_MORE_OR_EQUALS_TO'; - public static readonly OR: string = 'SYMBOL_OR'; - public static readonly AND: string = 'SYMBOL_AND'; - public static readonly INCREMENT: string = 'SYMBOL_INCREMENT'; - public static readonly DECREMENT: string = 'SYMBOL_DECREMENT'; -} - -export class KeywordType -{ - public static readonly BREAK: string = 'KEYWORD_BREAK'; - public static readonly RETURN: string = 'KEYWORD_RETURN'; - public static readonly CONTINUE: string = 'KEYWORD_CONTINUE'; - public static readonly FOR: string = 'KEYWORD_FOR'; - public static readonly WHILE: string = 'KEYWORD_WHILE'; - public static readonly VAR: string = 'KEYWORD_VAR'; - public static readonly CONST: string = 'KEYWORD_CONST'; - public static readonly IF: string = 'KEYWORD_IF'; - public static readonly ELSE: string = 'KEYWORD_ELSE'; - public static readonly ELSEIF: string = 'KEYWORD_ELSEIF'; - public static readonly PRINT: string = 'KEYWORD_PRINT'; - public static readonly READ: string = 'KEYWORD_READ'; -} \ No newline at end of file diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 23564f5..6a8a010 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -1,8 +1,27 @@ -import { SymbolType, Token, TokenType } from "../lexer/Token"; +import { VariableAccessNode } from './nodes/VariableAccessNode'; +import { ParserResult } from './ParserResult'; +import { ParserError, ParserErrors } from './ParserError'; +import { UnaryOperationNode } from './nodes/UnaryOperationNode'; +import { ParserNode } from './nodes/ParserNode'; +import { BinaryOperationNode } from './nodes/BinaryOperationNode'; +import { KeywordType, SymbolType, Token, TokenType } from "../lexer/Token"; import { NumberNode } from "./nodes/NumberNode"; +import { VariableAssignementNode } from './nodes/VariableAssignementNode'; + +//BY PRIORITY +const ATOMS: string[][] = [ + [TokenType.INTEGER, TokenType.FLOAT] +]; + +const FACTORS: string[][] = [ + [SymbolType.PLUS, SymbolType.MINUS], +]; -const FACTORS: string[] = [TokenType.INTEGER, TokenType.FLOAT]; const TERMS: string[] = [SymbolType.MULTIPLY, SymbolType.DIVIDE]; +const EXPRESSIONS: string[][] = [ + [KeywordType.VAR, KeywordType.CONST], + [SymbolType.PLUS, SymbolType.MINUS] +]; export class Parser { @@ -28,23 +47,133 @@ export class Parser public buildTree(): void { this.advance(); + const result = this.makeExpression(); + + if (result != null) console.log(result.getRepresentation()); } - private makeFactor(): NumberNode | null + private makeAtom(): ParserNode { - if(!FACTORS.includes(this.currentToken.deepType)) return null; + let node: ParserNode | null = null; + + if (this.currentToken) + { + if (ATOMS[0].includes(this.currentToken.deepType)) + { + node = new NumberNode(this.currentToken); + this.advance(); + } + else if (this.currentToken.deepType === TokenType.IDENTIFIER) + { + node = new VariableAccessNode(this.currentToken); + this.advance(); + } + else if (this.currentToken.deepType === SymbolType.LEFT_PARENTHESES) + { + this.advance(); + const expression: ParserNode = this.makeExpression(); + + if (this.currentToken.deepType === SymbolType.RIGHT_PARENTHESES) + { + this.advance(); + node = expression; + } + else throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); + } + } + + if (!node) throw new ParserError(ParserErrors.TOKEN_EXPECTED, [...ATOMS[0], ...FACTORS[0], SymbolType.LEFT_PARENTHESES].toString()); - this.advance(); - return new NumberNode(this.currentToken); + return node; } - private makeTerm(): void + private makePower(): ParserNode { - const leftFactor: NumberNode | null = this.makeFactor(); + let node: ParserNode = this.makeAtom(); - while(this.currentToken && TERMS.includes(this.currentToken.deepType)) + while (this.currentToken && this.currentToken.deepType === SymbolType.POWER) + { + const operator: Token = this.currentToken; + this.advance(); + + const rightFactor: ParserNode = this.makeFactor(); + node = new BinaryOperationNode(node, operator, rightFactor); + } + + return node; + } + + private makeFactor(): ParserNode + { + let node: ParserNode | null = null; + + if (this.currentToken) { - //const operator: + if (FACTORS[0].includes(this.currentToken.deepType)) + { + const cacheToken = this.currentToken; + this.advance(); + + node = new UnaryOperationNode(cacheToken, this.makeFactor()); + } + else + { + node = this.makePower(); + } } + + if (!node) throw new ParserError(ParserErrors.TOKEN_EXPECTED, [...FACTORS[0]].toString()); + + return node; + } + + private makeTerm(): ParserNode + { + let node: ParserNode = this.makeFactor(); + + while (this.currentToken && TERMS.includes(this.currentToken.deepType)) + { + const operator: Token = this.currentToken; + this.advance(); + + const rightFactor: ParserNode = this.makeFactor(); + node = new BinaryOperationNode(node, operator, rightFactor); + } + + return node; + } + + private makeExpression(): ParserNode + { + if (this.currentToken && EXPRESSIONS[0].includes(this.currentToken.deepType)) + { + this.advance(); + + if (!this.currentToken || this.currentToken.deepType !== TokenType.IDENTIFIER) throw new ParserError(ParserErrors.TOKEN_EXPECTED, TokenType.IDENTIFIER); + + const identifier: Token = this.currentToken; + this.advance(); + + if (!this.currentToken || this.currentToken.deepType !== SymbolType.ATTRIBUTION) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.ATTRIBUTION); + + this.advance(); + + const expression: ParserNode = this.makeExpression(); + + return new VariableAssignementNode(identifier, expression); + } + + let node: ParserNode = this.makeTerm(); + + while (this.currentToken && EXPRESSIONS[1].includes(this.currentToken.deepType)) + { + const operator: Token = this.currentToken; + this.advance(); + + const rightTerm: ParserNode = this.makeTerm(); + node = new BinaryOperationNode(node, operator, rightTerm); + } + + return node; } } \ No newline at end of file diff --git a/src/parser/ParserError.ts b/src/parser/ParserError.ts new file mode 100644 index 0000000..c8c3ac6 --- /dev/null +++ b/src/parser/ParserError.ts @@ -0,0 +1,21 @@ +export class ParserError +{ + name: string; + details: string; + + constructor(name: string, details: string) + { + this.name = name; + this.details = details; + } + + public asString(): string + { + return `[PARSER] ${this.name}: ${this.details}`; + } +} + +export class ParserErrors +{ + public static readonly TOKEN_EXPECTED: string = 'TOKEN_EXPECTED'; +} \ No newline at end of file diff --git a/src/parser/ParserResult.ts b/src/parser/ParserResult.ts new file mode 100644 index 0000000..9b71624 --- /dev/null +++ b/src/parser/ParserResult.ts @@ -0,0 +1,13 @@ +import { ParserNode } from './nodes/ParserNode'; + +export class ParserResult +{ + node: ParserNode | null = null; + + public success(result: ParserResult): ParserResult + { + this.node = result.node; + + return this; + } +} \ No newline at end of file diff --git a/src/parser/nodes/BinaryOperationNode.ts b/src/parser/nodes/BinaryOperationNode.ts index ad692b8..f906a27 100644 --- a/src/parser/nodes/BinaryOperationNode.ts +++ b/src/parser/nodes/BinaryOperationNode.ts @@ -1,14 +1,22 @@ import { Token } from "../../lexer/Token"; -import { ParserNode } from "./Node"; +import { ParserNode } from "./ParserNode"; -export class BinaryOperationNode +export class BinaryOperationNode extends ParserNode { leftNode: ParserNode; operationToken: Token; rightNode: ParserNode; - public toString(): string + constructor(leftNode: ParserNode, operationToken: Token, rightNode: ParserNode) { - return `(${this.leftNode.toString()}, ${this.operationToken.value}, ${this.rightNode.toString()})`; + super(); + this.leftNode = leftNode; + this.operationToken = operationToken; + this.rightNode = rightNode; + } + + public getRepresentation(): string + { + return `BinaryOperation: (${this.leftNode.getRepresentation()}, ${this.operationToken.value}, ${this.rightNode.getRepresentation()})`; } } \ No newline at end of file diff --git a/src/parser/nodes/Node.ts b/src/parser/nodes/Node.ts deleted file mode 100644 index 051cc5f..0000000 --- a/src/parser/nodes/Node.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Token } from "../../lexer/Token"; - -export class ParserNode -{ - token: Token; - - constructor(token: Token) - { - this.token = token; - } - - public toString(): string - { - return this.token.value; - } -} \ No newline at end of file diff --git a/src/parser/nodes/NumberNode.ts b/src/parser/nodes/NumberNode.ts index 873308e..2bb6e5d 100644 --- a/src/parser/nodes/NumberNode.ts +++ b/src/parser/nodes/NumberNode.ts @@ -1,6 +1,18 @@ -import { ParserNode } from "./Node"; +import { Token } from "../../lexer/Token"; +import { ParserNode } from "./ParserNode"; export class NumberNode extends ParserNode { + token: Token; + constructor(token: Token) + { + super(); + this.token = token; + } + + public getRepresentation(): string + { + return `Number: ${this.token.value}`; + } } \ No newline at end of file diff --git a/src/parser/nodes/ParserNode.ts b/src/parser/nodes/ParserNode.ts new file mode 100644 index 0000000..8bca399 --- /dev/null +++ b/src/parser/nodes/ParserNode.ts @@ -0,0 +1,7 @@ +export class ParserNode +{ + public getRepresentation(): string + { + return ''; + } +} \ No newline at end of file diff --git a/src/parser/nodes/UnaryOperationNode.ts b/src/parser/nodes/UnaryOperationNode.ts new file mode 100644 index 0000000..adf0be6 --- /dev/null +++ b/src/parser/nodes/UnaryOperationNode.ts @@ -0,0 +1,20 @@ +import { Token } from "../../lexer/Token"; +import { ParserNode } from "./ParserNode"; + +export class UnaryOperationNode extends ParserNode +{ + operationToken: Token; + node: ParserNode; + + constructor(operationToken: Token, node: ParserNode) + { + super(); + this.operationToken = operationToken; + this.node = node; + } + + public getRepresentation(): string + { + return `UnaryOperation: (${this.operationToken.value}, ${this.node.getRepresentation()})`; + } +} \ No newline at end of file diff --git a/src/parser/nodes/VariableAccessNode.ts b/src/parser/nodes/VariableAccessNode.ts new file mode 100644 index 0000000..af3598f --- /dev/null +++ b/src/parser/nodes/VariableAccessNode.ts @@ -0,0 +1,18 @@ +import { Token } from "../../lexer/Token"; +import { ParserNode } from "./ParserNode"; + +export class VariableAccessNode extends ParserNode +{ + variableToken: Token; + + constructor(variableToken: Token) + { + super(); + this.variableToken = variableToken; + } + + public getRepresentation(): string + { + return `VariableAccess: ${this.variableToken.value}`; + } +} \ No newline at end of file diff --git a/src/parser/nodes/VariableAssignementNode.ts b/src/parser/nodes/VariableAssignementNode.ts new file mode 100644 index 0000000..459be28 --- /dev/null +++ b/src/parser/nodes/VariableAssignementNode.ts @@ -0,0 +1,20 @@ +import { Token } from "../../lexer/Token"; +import { ParserNode } from "./ParserNode"; + +export class VariableAssignementNode extends ParserNode +{ + variableToken: Token; + node: ParserNode; + + constructor(variableToken: Token, node: ParserNode) + { + super(); + this.variableToken = variableToken; + this.node = node; + } + + public getRepresentation(): string + { + return `VariableAssignement: (${this.variableToken.value}, ${this.node.getRepresentation()})`; + } +} \ No newline at end of file From 07cbc0842905a161676d3e02802ca6e80eafe46f Mon Sep 17 00:00:00 2001 From: MyNameIsBatman Date: Mon, 30 May 2022 17:53:10 -0300 Subject: [PATCH 3/7] updates --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 05f1132..58324d4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import fs from 'fs'; import path from 'path'; import { Parser } from "./parser/Parser"; -const FILENAME: string = '_examples/somaso.txt'; +const FILENAME: string = '_examples/SomaDoisNumeros.txt'; const filePath: string = path.join(__dirname, FILENAME); From d4d7a78d65995655e20a630cd7dc3c9173790069 Mon Sep 17 00:00:00 2001 From: MyNameIsBatman Date: Tue, 31 May 2022 08:18:33 -0300 Subject: [PATCH 4/7] Updates --- "docs/Gram\303\241tica.txt" | 17 +++- src/_examples/SomaDoisNumeros.txt | 18 ++-- src/_examples/somaso.txt | 4 +- src/lexer/Lexer.ts | 24 +++-- src/lexer/Token.ts | 4 +- src/parser/Parser.ts | 105 ++++++++++++++++++-- src/parser/nodes/BinaryOperationNode.ts | 4 +- src/parser/nodes/CallingNode.ts | 23 +++++ src/parser/nodes/NumberNode.ts | 2 +- src/parser/nodes/ParserNode.ts | 2 +- src/parser/nodes/StringNode.ts | 18 ++++ src/parser/nodes/UnaryOperationNode.ts | 4 +- src/parser/nodes/VariableAccessNode.ts | 2 +- src/parser/nodes/VariableAssignementNode.ts | 4 +- 14 files changed, 189 insertions(+), 42 deletions(-) create mode 100644 src/parser/nodes/CallingNode.ts create mode 100644 src/parser/nodes/StringNode.ts diff --git "a/docs/Gram\303\241tica.txt" "b/docs/Gram\303\241tica.txt" index 9e2b814..e6ecaf8 100644 --- "a/docs/Gram\303\241tica.txt" +++ "b/docs/Gram\303\241tica.txt" @@ -1,3 +1,7 @@ +programa : declaração* + +declaração : expressao (EOL|EOF) + expressao : VAR|CONST IDENTIFIER EQ expressao : termo ((MAIS|MENOS) termo)* @@ -6,7 +10,14 @@ termo : fator ((MULTIPLICAR|DIVIDIR) fator)* fator : (MAIS|MENOS) fator : power -power : atom (POWER fator)* +power : call (POWER fator)* + +call : atom (LPAREN (expressão (COMMA expressão)*)? RPAREN)? + +atom : INT|FLOAT|STRING|IDENTIFIER + : LPAREN expressão RPAREN + : expressão_if -atom : INT|FLOAT|IDENTIFIER - : LEFT_PARETENSYS fator RIGHT_PARENTESYS \ No newline at end of file +expressão_if: IF LPAREN expressão RPAREN LBRACE expressão RBRACE + (ELSEIF LPAREN expressão RPAREN LBRACE expressão RBRACE)* + (ELSE expressão)? \ No newline at end of file diff --git a/src/_examples/SomaDoisNumeros.txt b/src/_examples/SomaDoisNumeros.txt index 7212efe..c8aaf22 100644 --- a/src/_examples/SomaDoisNumeros.txt +++ b/src/_examples/SomaDoisNumeros.txt @@ -1,16 +1,16 @@ -var valor1 = 0; -var valor2 = 0; +var valor1 = 0 +var valor2 = 0 -print("Digite o valor 1 de um número inteiro"); -read(valor1); +print("Digite o valor 1 de um número inteiro") +read(valor1) -print("Digite o valor 2 de um número inteiro"); -read(valor2); +print("Digite o valor 2 de um número inteiro") +read(valor2) //Processamento do programa -const resultado = valor1 + valor2; +const resultado = valor1 + valor2 if(resultado >= 10) - print("O valor é maior ou igual a 10. O resultado é " + resultado); + print("O valor é maior ou igual a 10. O resultado é " + resultado) else - print("O valor é menor que 10. O resultado é " + resultado); \ No newline at end of file + print("O valor é menor que 10. O resultado é " + resultado) \ No newline at end of file diff --git a/src/_examples/somaso.txt b/src/_examples/somaso.txt index 5d4b038..02f04b0 100644 --- a/src/_examples/somaso.txt +++ b/src/_examples/somaso.txt @@ -1 +1,3 @@ -vars resultado = (1+2)*3+4+abc+6+7+8^2 \ No newline at end of file +var valor1 = 10 * 5 + (-40) +var valor2 = 0 +funcs("abc") \ No newline at end of file diff --git a/src/lexer/Lexer.ts b/src/lexer/Lexer.ts index 8a51dcc..193d84c 100644 --- a/src/lexer/Lexer.ts +++ b/src/lexer/Lexer.ts @@ -10,13 +10,13 @@ const CHARS: string[] = [...STARTING_CHARS, ...STARTING_DIGITS, '_']; const STRING_INIT_SYMBOL: string = '"'; const STRING_STOP_SYMBOL: string = '"'; const COMMENT_INIT_SYMBOL: string = '//'; -const COMMENT_STOP_SYMBOL: string = '\n'; +const END_OF_LINE_SYMBOL: string = '\n'; const SYMBOLS: string[] = ['\'', '*', '-', '+', '/', ';', '{', '}', '(', ')', '=', '|', '&', '%', '!', '<', '>', '^']; const COMPOUND_SYMBOLS: string[] = [ '--', '++', '==', '===', '!=', '<=', '>=', '||', '&&']; -const KEYWORDS: string[] = ['break','return','continue','for','while', 'var', 'const', 'if', 'else', 'elseif', 'print', 'read']; -const IGNORED_TOKENS: string[] = ['\n', '\t', '\r', ' ']; +const KEYWORDS: string[] = ['break','return','continue','for','while', 'var', 'const', 'if', 'else', 'elseif']; +const IGNORED_TOKENS: string[] = ['\t', '\r', ' ']; export class Lexer { @@ -52,6 +52,13 @@ export class Lexer continue; } + if(END_OF_LINE_SYMBOL === this.currentChar) + { + tokens.push(new Token(TokenType.END_OF_LINE, null)); + this.advance(); + continue; + } + if(IGNORED_TOKENS.includes(this.currentChar)) { this.advance(); @@ -115,16 +122,10 @@ export class Lexer private makeComment(): void { - this.advance(); - - while(this.currentChar) + do { - if(this.currentChar === COMMENT_STOP_SYMBOL) break; - this.advance(); - } - - if(this.currentChar === COMMENT_STOP_SYMBOL) this.advance(); + } while(this.currentChar && this.currentChar !== END_OF_LINE_SYMBOL); } private makeIdentifier(): Token @@ -214,6 +215,7 @@ export class Lexer case '%': return SymbolType.PERCENTAGE; case '!': return SymbolType.EXCLAMATION; case '^': return SymbolType.POWER; + case ',': return SymbolType.COMMA; case '==': return SymbolType.EQUALS; case '===': return SymbolType.EXACTLY_EQUALS; diff --git a/src/lexer/Token.ts b/src/lexer/Token.ts index 4d6b297..147c5ce 100644 --- a/src/lexer/Token.ts +++ b/src/lexer/Token.ts @@ -26,6 +26,7 @@ export class TokenType public static readonly FLOAT: string = 'FLOAT'; public static readonly STRING: string = 'STRING'; public static readonly IDENTIFIER: string = 'IDENTIFIER'; + public static readonly END_OF_LINE: string = 'EOL'; public static readonly END_OF_FILE: string = 'EOF'; } @@ -47,6 +48,7 @@ export class SymbolType public static readonly PERCENTAGE: string = 'SYMBOL_PERCENTAGE'; public static readonly EXCLAMATION: string = 'SYMBOL_EXCLAMATION'; public static readonly POWER: string = 'SYMBOL_POWER'; + public static readonly COMMA: string = 'SYMBOL_COMMA'; public static readonly EQUALS: string = 'SYMBOL_EQUALS'; public static readonly EXACTLY_EQUALS: string = 'SYMBOL_EXACTLY_EQUALS'; @@ -71,6 +73,4 @@ export class KeywordType public static readonly IF: string = 'KEYWORD_IF'; public static readonly ELSE: string = 'KEYWORD_ELSE'; public static readonly ELSEIF: string = 'KEYWORD_ELSEIF'; - public static readonly PRINT: string = 'KEYWORD_PRINT'; - public static readonly READ: string = 'KEYWORD_READ'; } \ No newline at end of file diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 6a8a010..0faf0dc 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -1,5 +1,6 @@ import { VariableAccessNode } from './nodes/VariableAccessNode'; -import { ParserResult } from './ParserResult'; +import { CallingNode } from './nodes/CallingNode'; +import { StringNode } from './nodes/StringNode'; import { ParserError, ParserErrors } from './ParserError'; import { UnaryOperationNode } from './nodes/UnaryOperationNode'; import { ParserNode } from './nodes/ParserNode'; @@ -10,7 +11,8 @@ import { VariableAssignementNode } from './nodes/VariableAssignementNode'; //BY PRIORITY const ATOMS: string[][] = [ - [TokenType.INTEGER, TokenType.FLOAT] + [TokenType.INTEGER, TokenType.FLOAT], + [TokenType.STRING] ]; const FACTORS: string[][] = [ @@ -47,9 +49,21 @@ export class Parser public buildTree(): void { this.advance(); - const result = this.makeExpression(); + const result = this.makeProgram(); - if (result != null) console.log(result.getRepresentation()); + if (result != null) + { + const table: { representation: string }[] = []; + + for (const item of result) table.push({ representation: item.representation }); + + console.table(table); + } + } + + private makeIf(): void + { + } private makeAtom(): ParserNode @@ -63,6 +77,11 @@ export class Parser node = new NumberNode(this.currentToken); this.advance(); } + else if (ATOMS[1].includes(this.currentToken.deepType)) + { + node = new StringNode(this.currentToken); + this.advance(); + } else if (this.currentToken.deepType === TokenType.IDENTIFIER) { node = new VariableAccessNode(this.currentToken); @@ -80,16 +99,55 @@ export class Parser } else throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); } - } + else if (this.currentToken.deepType === KeywordType.IF) + { - if (!node) throw new ParserError(ParserErrors.TOKEN_EXPECTED, [...ATOMS[0], ...FACTORS[0], SymbolType.LEFT_PARENTHESES].toString()); + } + } + console.log(this.currentToken); + if (!node) throw new ParserError(ParserErrors.TOKEN_EXPECTED, [...ATOMS[0], ...ATOMS[1], ...FACTORS[0], TokenType.IDENTIFIER, SymbolType.LEFT_PARENTHESES].toString()); return node; } - private makePower(): ParserNode + private makeCall(): ParserNode { let node: ParserNode = this.makeAtom(); + + if (this.currentToken && this.currentToken.deepType === SymbolType.LEFT_PARENTHESES) + { + this.advance(); + const argumentsNodes: ParserNode[] = []; + + if (this.currentToken && this.currentToken.deepType === SymbolType.RIGHT_PARENTHESES) + { + this.advance(); + } + else + { + argumentsNodes.push(this.makeExpression()); + + while (this.currentToken && this.currentToken.deepType === SymbolType.COMMA) + { + this.advance(); + + argumentsNodes.push(this.makeExpression()); + } + + if (!this.currentToken || this.currentToken.deepType !== SymbolType.RIGHT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); + + this.advance(); + } + + return new CallingNode(node, argumentsNodes); + } + + return node; + } + + private makePower(): ParserNode + { + let node: ParserNode = this.makeCall(); while (this.currentToken && this.currentToken.deepType === SymbolType.POWER) { @@ -175,5 +233,38 @@ export class Parser } return node; + + } + + public makeStatement(): ParserNode + { + const expression: ParserNode = this.makeExpression(); + + if (!this.currentToken || ![TokenType.END_OF_LINE, TokenType.END_OF_FILE].includes(this.currentToken.deepType)) + { + throw new ParserError(ParserErrors.TOKEN_EXPECTED, [TokenType.END_OF_LINE, TokenType.END_OF_FILE].toString()); + } + + return expression; + } + + public makeProgram(): ParserNode[] + { + const nodes: ParserNode[] = []; + + while(this.currentToken) + { + if (this.currentToken.deepType === TokenType.END_OF_FILE) break; + + if (this.currentToken.deepType === TokenType.END_OF_LINE) + { + this.advance(); + continue; + } + + nodes.push(this.makeStatement()); + } + + return nodes; } } \ No newline at end of file diff --git a/src/parser/nodes/BinaryOperationNode.ts b/src/parser/nodes/BinaryOperationNode.ts index f906a27..09a82d8 100644 --- a/src/parser/nodes/BinaryOperationNode.ts +++ b/src/parser/nodes/BinaryOperationNode.ts @@ -15,8 +15,8 @@ export class BinaryOperationNode extends ParserNode this.rightNode = rightNode; } - public getRepresentation(): string + public get representation(): string { - return `BinaryOperation: (${this.leftNode.getRepresentation()}, ${this.operationToken.value}, ${this.rightNode.getRepresentation()})`; + return `BinaryOperation: (${this.leftNode.representation}, ${this.operationToken.value}, ${this.rightNode.representation})`; } } \ No newline at end of file diff --git a/src/parser/nodes/CallingNode.ts b/src/parser/nodes/CallingNode.ts new file mode 100644 index 0000000..3120851 --- /dev/null +++ b/src/parser/nodes/CallingNode.ts @@ -0,0 +1,23 @@ +import { ParserNode } from "./ParserNode"; + +export class CallingNode extends ParserNode +{ + identifierNode: ParserNode; + argumentsNodes: ParserNode[]; + + constructor(identifierNode: ParserNode, argumentsNodes: ParserNode[]) + { + super(); + this.identifierNode = identifierNode; + this.argumentsNodes = argumentsNodes; + } + + public get representation(): string + { + var argumentsReps: string[] = []; + + for(const argument of this.argumentsNodes) argumentsReps.push(argument.representation); + + return `Call: ${this.identifierNode.representation}(${argumentsReps.join(',')})`; + } +} \ No newline at end of file diff --git a/src/parser/nodes/NumberNode.ts b/src/parser/nodes/NumberNode.ts index 2bb6e5d..2fef4aa 100644 --- a/src/parser/nodes/NumberNode.ts +++ b/src/parser/nodes/NumberNode.ts @@ -11,7 +11,7 @@ export class NumberNode extends ParserNode this.token = token; } - public getRepresentation(): string + public get representation(): string { return `Number: ${this.token.value}`; } diff --git a/src/parser/nodes/ParserNode.ts b/src/parser/nodes/ParserNode.ts index 8bca399..1c44ffb 100644 --- a/src/parser/nodes/ParserNode.ts +++ b/src/parser/nodes/ParserNode.ts @@ -1,6 +1,6 @@ export class ParserNode { - public getRepresentation(): string + public get representation(): string { return ''; } diff --git a/src/parser/nodes/StringNode.ts b/src/parser/nodes/StringNode.ts new file mode 100644 index 0000000..f3c49a4 --- /dev/null +++ b/src/parser/nodes/StringNode.ts @@ -0,0 +1,18 @@ +import { Token } from "../../lexer/Token"; +import { ParserNode } from "./ParserNode"; + +export class StringNode extends ParserNode +{ + token: Token; + + constructor(token: Token) + { + super(); + this.token = token; + } + + public get representation(): string + { + return `String: "${this.token.value}"`; + } +} \ No newline at end of file diff --git a/src/parser/nodes/UnaryOperationNode.ts b/src/parser/nodes/UnaryOperationNode.ts index adf0be6..70990b2 100644 --- a/src/parser/nodes/UnaryOperationNode.ts +++ b/src/parser/nodes/UnaryOperationNode.ts @@ -13,8 +13,8 @@ export class UnaryOperationNode extends ParserNode this.node = node; } - public getRepresentation(): string + public get representation(): string { - return `UnaryOperation: (${this.operationToken.value}, ${this.node.getRepresentation()})`; + return `UnaryOperation: (${this.operationToken.value}, ${this.node.representation})`; } } \ No newline at end of file diff --git a/src/parser/nodes/VariableAccessNode.ts b/src/parser/nodes/VariableAccessNode.ts index af3598f..0d07193 100644 --- a/src/parser/nodes/VariableAccessNode.ts +++ b/src/parser/nodes/VariableAccessNode.ts @@ -11,7 +11,7 @@ export class VariableAccessNode extends ParserNode this.variableToken = variableToken; } - public getRepresentation(): string + public get representation(): string { return `VariableAccess: ${this.variableToken.value}`; } diff --git a/src/parser/nodes/VariableAssignementNode.ts b/src/parser/nodes/VariableAssignementNode.ts index 459be28..8b46b74 100644 --- a/src/parser/nodes/VariableAssignementNode.ts +++ b/src/parser/nodes/VariableAssignementNode.ts @@ -13,8 +13,8 @@ export class VariableAssignementNode extends ParserNode this.node = node; } - public getRepresentation(): string + public get representation(): string { - return `VariableAssignement: (${this.variableToken.value}, ${this.node.getRepresentation()})`; + return `VariableAssignement: (${this.variableToken.value}, ${this.node.representation})`; } } \ No newline at end of file From 043ae5a17a491b3453ec5b957ce97db3ad0c298f Mon Sep 17 00:00:00 2001 From: MyNameIsBatman Date: Tue, 31 May 2022 16:34:05 -0300 Subject: [PATCH 5/7] Updates --- "docs/Gram\303\241tica.txt" | 24 +- src/_examples/ProfMedia.txt | 19 ++ src/_examples/ProfTabuada.txt | 25 ++ src/_examples/SomaDoisNumeros.txt | 16 - src/_examples/somaso.txt | 3 - src/index.ts | 8 +- src/lexer/Lexer.ts | 8 +- src/lexer/Token.ts | 5 + src/parser/Parser.ts | 337 ++++++++++++++++---- src/parser/nodes/BinaryOperationNode.ts | 2 +- src/parser/nodes/CallingNode.ts | 2 +- src/parser/nodes/CodeBlockNode.ts | 21 ++ src/parser/nodes/ForNode.ts | 25 ++ src/parser/nodes/FunctionAssignementNode.ts | 26 ++ src/parser/nodes/IfCase.ts | 18 ++ src/parser/nodes/IfNode.ts | 24 ++ src/parser/nodes/NumberNode.ts | 2 +- src/parser/nodes/StringNode.ts | 2 +- src/parser/nodes/UnaryOperationNode.ts | 2 +- src/parser/nodes/VariableAccessNode.ts | 2 +- src/parser/nodes/VariableAssignementNode.ts | 2 +- src/parser/nodes/WhileNode.ts | 18 ++ 22 files changed, 488 insertions(+), 103 deletions(-) create mode 100644 src/_examples/ProfMedia.txt create mode 100644 src/_examples/ProfTabuada.txt delete mode 100644 src/_examples/SomaDoisNumeros.txt delete mode 100644 src/_examples/somaso.txt create mode 100644 src/parser/nodes/CodeBlockNode.ts create mode 100644 src/parser/nodes/ForNode.ts create mode 100644 src/parser/nodes/FunctionAssignementNode.ts create mode 100644 src/parser/nodes/IfCase.ts create mode 100644 src/parser/nodes/IfNode.ts create mode 100644 src/parser/nodes/WhileNode.ts diff --git "a/docs/Gram\303\241tica.txt" "b/docs/Gram\303\241tica.txt" index e6ecaf8..e5574b3 100644 --- "a/docs/Gram\303\241tica.txt" +++ "b/docs/Gram\303\241tica.txt" @@ -1,9 +1,13 @@ -programa : declaração* +bloco_codigo: LBRACE expressão* RBRACE -declaração : expressao (EOL|EOF) +expressão : FUNCTION IDENTIFIER LPAREN (IDENTIFIER (COMMA IDENTIFIER)*)? RPAREN bloco_codigo + : VAR|CONST IDENTIFIER EQ expressão + : expressão_co ((AND|OR) expressão_co)* -expressao : VAR|CONST IDENTIFIER EQ expressao - : termo ((MAIS|MENOS) termo)* +expressão_co: NOT expressão_co + : expressão_ar ((IGUAL|N_IGUAL|ME_QUE|MA_QUE|ME_IG_QUE|MA_IG_QUE) expressão_ar)* + +expressão_ar: termo ((MAIS|MENOS) termo)* termo : fator ((MULTIPLICAR|DIVIDIR) fator)* @@ -17,7 +21,13 @@ call : atom (LPAREN (expressão (COMMA expressão)*)? RPAREN)? atom : INT|FLOAT|STRING|IDENTIFIER : LPAREN expressão RPAREN : expressão_if + : expressão_fo + : expressão_wh + +expressão_if: IF LPAREN expressão RPAREN bloco_codigo + (ELSEIF LPAREN expressão RPAREN bloco_codigo)* + (ELSE bloco_codigo)? + +expressão_fo: FOR LPAREN IDENTIFIER EQ expressão TO expressão STEP expressão RPAREN bloco_codigo -expressão_if: IF LPAREN expressão RPAREN LBRACE expressão RBRACE - (ELSEIF LPAREN expressão RPAREN LBRACE expressão RBRACE)* - (ELSE expressão)? \ No newline at end of file +expressão_wh: WHILE LPAREN expressão RPAREN bloco_codigo \ No newline at end of file diff --git a/src/_examples/ProfMedia.txt b/src/_examples/ProfMedia.txt new file mode 100644 index 0000000..90d9de5 --- /dev/null +++ b/src/_examples/ProfMedia.txt @@ -0,0 +1,19 @@ +{ + var valor1 = 0 + var valor2 = 0 + + print("Digite o valor 1 de um número inteiro") + read(valor1) + + print("Digite o valor 2 de um número inteiro") + read(valor2) + + //Processamento do programa + const resultado = valor1 + valor2 + + if(resultado >= 10) { + print("O valor é maior ou igual a 10. O resultado é " + resultado) + } else { + print("O valor é menor que 10. O resultado é " + resultado) + } +} \ No newline at end of file diff --git a/src/_examples/ProfTabuada.txt b/src/_examples/ProfTabuada.txt new file mode 100644 index 0000000..d14a9cc --- /dev/null +++ b/src/_examples/ProfTabuada.txt @@ -0,0 +1,25 @@ +{ + var tabuada = 0 + var nome = "" + + escreva("Digite o seu nome") + leia(nome) + + escreva("Tabuada que deseja calcular") + leia(tabuada) + + resolverTabuada(tabuada) + + escreva(nome + ". Foi resolvida a tabuada do " + tabuada) + + function resolverTabuada(tabuada) { + var resto = 0 + + for(i = 0 to 20 step 1) { + if(resto == 0) { + escreva(tabuada + " X " + i + " = " + tabuada * i) + escreva(tabuada + " X " + i + " = " + tabuada * i) + } + } + } +} \ No newline at end of file diff --git a/src/_examples/SomaDoisNumeros.txt b/src/_examples/SomaDoisNumeros.txt deleted file mode 100644 index c8aaf22..0000000 --- a/src/_examples/SomaDoisNumeros.txt +++ /dev/null @@ -1,16 +0,0 @@ -var valor1 = 0 -var valor2 = 0 - -print("Digite o valor 1 de um número inteiro") -read(valor1) - -print("Digite o valor 2 de um número inteiro") -read(valor2) - -//Processamento do programa -const resultado = valor1 + valor2 - -if(resultado >= 10) - print("O valor é maior ou igual a 10. O resultado é " + resultado) -else - print("O valor é menor que 10. O resultado é " + resultado) \ No newline at end of file diff --git a/src/_examples/somaso.txt b/src/_examples/somaso.txt deleted file mode 100644 index 02f04b0..0000000 --- a/src/_examples/somaso.txt +++ /dev/null @@ -1,3 +0,0 @@ -var valor1 = 10 * 5 + (-40) -var valor2 = 0 -funcs("abc") \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 58324d4..c8782e6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,12 @@ +import { ParserNode } from './parser/nodes/ParserNode'; +import { CodeBlockNode } from './parser/nodes/CodeBlockNode'; import { Lexer } from "./lexer/Lexer"; import { Token } from "./lexer/Token"; import fs from 'fs'; import path from 'path'; import { Parser } from "./parser/Parser"; -const FILENAME: string = '_examples/SomaDoisNumeros.txt'; +const FILENAME: string = '_examples/ProfMedia.txt'; const filePath: string = path.join(__dirname, FILENAME); @@ -16,7 +18,9 @@ fs.readFile(filePath, {encoding: 'utf-8'}, (err, data) => { console.table(tokens); const parser: Parser = new Parser(tokens); - parser.buildTree(); + const codeBlock: ParserNode = parser.makeCodeBlock(); + + if (codeBlock != null) console.log(codeBlock.representation); } else { console.error(err); } diff --git a/src/lexer/Lexer.ts b/src/lexer/Lexer.ts index 193d84c..8859173 100644 --- a/src/lexer/Lexer.ts +++ b/src/lexer/Lexer.ts @@ -10,12 +10,13 @@ const CHARS: string[] = [...STARTING_CHARS, ...STARTING_DIGITS, '_']; const STRING_INIT_SYMBOL: string = '"'; const STRING_STOP_SYMBOL: string = '"'; const COMMENT_INIT_SYMBOL: string = '//'; +const COMMENT_STOP_SYMBOL: string = '' const END_OF_LINE_SYMBOL: string = '\n'; const SYMBOLS: string[] = ['\'', '*', '-', '+', '/', ';', '{', '}', '(', ')', '=', '|', '&', '%', '!', '<', '>', '^']; const COMPOUND_SYMBOLS: string[] = [ '--', '++', '==', '===', '!=', '<=', '>=', '||', '&&']; -const KEYWORDS: string[] = ['break','return','continue','for','while', 'var', 'const', 'if', 'else', 'elseif']; +const KEYWORDS: string[] = ['break','return','continue','for', 'to', 'step', 'while', 'var', 'const', 'function', 'if', 'else', 'elseif']; const IGNORED_TOKENS: string[] = ['\t', '\r', ' ']; export class Lexer @@ -216,6 +217,8 @@ export class Lexer case '!': return SymbolType.EXCLAMATION; case '^': return SymbolType.POWER; case ',': return SymbolType.COMMA; + case '>': return SymbolType.MORE_THAN; + case '<': return SymbolType.LESS_THAN; case '==': return SymbolType.EQUALS; case '===': return SymbolType.EXACTLY_EQUALS; @@ -239,9 +242,12 @@ export class Lexer case 'return': return KeywordType.RETURN; case 'continue': return KeywordType.CONTINUE; case 'for': return KeywordType.FOR; + case 'to': return KeywordType.TO; + case 'step': return KeywordType.STEP; case 'while': return KeywordType.WHILE; case 'var': return KeywordType.VAR; case 'const': return KeywordType.CONST; + case 'function': return KeywordType.FUNCTION; case 'if': return KeywordType.IF; case 'else': return KeywordType.ELSE; case 'elseif': return KeywordType.ELSEIF; diff --git a/src/lexer/Token.ts b/src/lexer/Token.ts index 147c5ce..0038788 100644 --- a/src/lexer/Token.ts +++ b/src/lexer/Token.ts @@ -49,6 +49,8 @@ export class SymbolType public static readonly EXCLAMATION: string = 'SYMBOL_EXCLAMATION'; public static readonly POWER: string = 'SYMBOL_POWER'; public static readonly COMMA: string = 'SYMBOL_COMMA'; + public static readonly MORE_THAN: string = 'SYMBOL_MORE_THAN'; + public static readonly LESS_THAN: string = 'SYMBOL_LESS_THAN'; public static readonly EQUALS: string = 'SYMBOL_EQUALS'; public static readonly EXACTLY_EQUALS: string = 'SYMBOL_EXACTLY_EQUALS'; @@ -67,9 +69,12 @@ export class KeywordType public static readonly RETURN: string = 'KEYWORD_RETURN'; public static readonly CONTINUE: string = 'KEYWORD_CONTINUE'; public static readonly FOR: string = 'KEYWORD_FOR'; + public static readonly TO: string = 'KEYWORD_TO'; + public static readonly STEP: string = 'KEYWORD_STEP'; public static readonly WHILE: string = 'KEYWORD_WHILE'; public static readonly VAR: string = 'KEYWORD_VAR'; public static readonly CONST: string = 'KEYWORD_CONST'; + public static readonly FUNCTION: string = 'KEYWORD_FUNCTION'; public static readonly IF: string = 'KEYWORD_IF'; public static readonly ELSE: string = 'KEYWORD_ELSE'; public static readonly ELSEIF: string = 'KEYWORD_ELSEIF'; diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 0faf0dc..0e6d11a 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -1,3 +1,6 @@ +import { ForNode } from './nodes/ForNode'; +import { IfNode } from './nodes/IfNode'; +import { IfCase } from './nodes/IfCase'; import { VariableAccessNode } from './nodes/VariableAccessNode'; import { CallingNode } from './nodes/CallingNode'; import { StringNode } from './nodes/StringNode'; @@ -8,6 +11,9 @@ import { BinaryOperationNode } from './nodes/BinaryOperationNode'; import { KeywordType, SymbolType, Token, TokenType } from "../lexer/Token"; import { NumberNode } from "./nodes/NumberNode"; import { VariableAssignementNode } from './nodes/VariableAssignementNode'; +import { WhileNode } from './nodes/WhileNode'; +import { FunctionAssignementNode } from './nodes/FunctionAssignementNode'; +import { CodeBlockNode } from './nodes/CodeBlockNode'; //BY PRIORITY const ATOMS: string[][] = [ @@ -22,7 +28,7 @@ const FACTORS: string[][] = [ const TERMS: string[] = [SymbolType.MULTIPLY, SymbolType.DIVIDE]; const EXPRESSIONS: string[][] = [ [KeywordType.VAR, KeywordType.CONST], - [SymbolType.PLUS, SymbolType.MINUS] + [SymbolType.AND, SymbolType.OR] ]; export class Parser @@ -33,12 +39,17 @@ export class Parser constructor(tokens: Token[]) { this.tokens = tokens; - this.currentPosition = -1; + this.currentPosition = 0; } - private get currentToken(): Token | null + private get currentToken(): Token { - return this.tokens[this.currentPosition] || null + return this.tokens[this.currentPosition] + } + + private get currentType(): string + { + return this.currentToken ? this.currentToken.deepType : ''; } private advance(): void @@ -46,24 +57,128 @@ export class Parser this.currentPosition++; } - public buildTree(): void + private makeWhile(): ParserNode { + if (this.currentType !== SymbolType.LEFT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.LEFT_PARENTHESES); + this.advance(); - const result = this.makeProgram(); - if (result != null) - { - const table: { representation: string }[] = []; + const condition: ParserNode = this.makeExpression(); - for (const item of result) table.push({ representation: item.representation }); + if (this.currentType !== SymbolType.RIGHT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); - console.table(table); - } + this.advance(); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + + const body: ParserNode = this.makeCodeBlock(); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + + return new WhileNode(condition, body); } - private makeIf(): void + private makeFor(): ParserNode { - + if (this.currentType !== SymbolType.LEFT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.LEFT_PARENTHESES); + + this.advance(); + + if (this.currentType !== TokenType.IDENTIFIER) throw new ParserError(ParserErrors.TOKEN_EXPECTED, TokenType.IDENTIFIER); + + const identifier: Token = this.currentToken; + + this.advance(); + + if (this.currentType !== SymbolType.ATTRIBUTION) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.ATTRIBUTION); + + this.advance(); + + const initialValue: ParserNode = this.makeExpression(); + + if (this.currentType !== KeywordType.TO) throw new ParserError(ParserErrors.TOKEN_EXPECTED, KeywordType.TO); + + this.advance(); + + const targetValue: ParserNode = this.makeExpression(); + + if (this.currentType !== KeywordType.STEP) throw new ParserError(ParserErrors.TOKEN_EXPECTED, KeywordType.STEP); + + this.advance(); + + const step: ParserNode = this.makeExpression(); + + if (this.currentType !== SymbolType.RIGHT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); + + this.advance(); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + + const body: ParserNode = this.makeCodeBlock(); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + + return new ForNode(identifier, initialValue, targetValue, step, body); + } + + private makeIf(): ParserNode + { + const cases: IfCase[] = []; + let elseCase: ParserNode | null = null; + + if (this.currentType !== SymbolType.LEFT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.LEFT_PARENTHESES); + + this.advance(); + + const condition: ParserNode = this.makeExpression(); + + if (this.currentType !== SymbolType.RIGHT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); + + this.advance(); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + + const body: ParserNode = this.makeCodeBlock(); + + cases.push(new IfCase(condition, body)); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + + while (this.currentType === KeywordType.ELSEIF) + { + this.advance(); + + if (this.currentType !== SymbolType.LEFT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.LEFT_PARENTHESES); + + this.advance(); + + const condition: ParserNode = this.makeExpression(); + + if (this.currentType !== SymbolType.RIGHT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); + + this.advance(); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + + const body: ParserNode = this.makeCodeBlock(); + + cases.push(new IfCase(condition, body)); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + } + + if (this.currentType === KeywordType.ELSE) + { + this.advance(); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + + elseCase = this.makeCodeBlock(); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + } + + return new IfNode(cases, elseCase); } private makeAtom(): ParserNode @@ -72,40 +187,51 @@ export class Parser if (this.currentToken) { - if (ATOMS[0].includes(this.currentToken.deepType)) + if (ATOMS[0].includes(this.currentType)) { node = new NumberNode(this.currentToken); this.advance(); } - else if (ATOMS[1].includes(this.currentToken.deepType)) + else if (ATOMS[1].includes(this.currentType)) { node = new StringNode(this.currentToken); this.advance(); } - else if (this.currentToken.deepType === TokenType.IDENTIFIER) + else if (this.currentType === TokenType.IDENTIFIER) { node = new VariableAccessNode(this.currentToken); this.advance(); } - else if (this.currentToken.deepType === SymbolType.LEFT_PARENTHESES) + else if (this.currentType === SymbolType.LEFT_PARENTHESES) { this.advance(); const expression: ParserNode = this.makeExpression(); - if (this.currentToken.deepType === SymbolType.RIGHT_PARENTHESES) + if (this.currentType === SymbolType.RIGHT_PARENTHESES) { this.advance(); node = expression; } else throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); } - else if (this.currentToken.deepType === KeywordType.IF) + else if (this.currentType === KeywordType.IF) { - + this.advance(); + node = this.makeIf(); + } + else if (this.currentType === KeywordType.FOR) + { + this.advance(); + node = this.makeFor(); + } + else if (this.currentType === KeywordType.WHILE) + { + this.advance(); + node = this.makeWhile(); } } - console.log(this.currentToken); - if (!node) throw new ParserError(ParserErrors.TOKEN_EXPECTED, [...ATOMS[0], ...ATOMS[1], ...FACTORS[0], TokenType.IDENTIFIER, SymbolType.LEFT_PARENTHESES].toString()); + + if (!node) throw new ParserError(ParserErrors.TOKEN_EXPECTED, [...ATOMS[0], ...ATOMS[1], ...FACTORS[0], KeywordType.IF, TokenType.IDENTIFIER, SymbolType.LEFT_PARENTHESES].toString()); return node; } @@ -114,12 +240,12 @@ export class Parser { let node: ParserNode = this.makeAtom(); - if (this.currentToken && this.currentToken.deepType === SymbolType.LEFT_PARENTHESES) + if (this.currentType === SymbolType.LEFT_PARENTHESES) { this.advance(); const argumentsNodes: ParserNode[] = []; - if (this.currentToken && this.currentToken.deepType === SymbolType.RIGHT_PARENTHESES) + if (this.currentType === SymbolType.RIGHT_PARENTHESES) { this.advance(); } @@ -127,14 +253,14 @@ export class Parser { argumentsNodes.push(this.makeExpression()); - while (this.currentToken && this.currentToken.deepType === SymbolType.COMMA) + while (this.currentType === SymbolType.COMMA) { this.advance(); argumentsNodes.push(this.makeExpression()); } - if (!this.currentToken || this.currentToken.deepType !== SymbolType.RIGHT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); + if (this.currentType !== SymbolType.RIGHT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); this.advance(); } @@ -149,7 +275,7 @@ export class Parser { let node: ParserNode = this.makeCall(); - while (this.currentToken && this.currentToken.deepType === SymbolType.POWER) + while (this.currentType === SymbolType.POWER) { const operator: Token = this.currentToken; this.advance(); @@ -165,19 +291,16 @@ export class Parser { let node: ParserNode | null = null; - if (this.currentToken) + if (FACTORS[0].includes(this.currentType)) { - if (FACTORS[0].includes(this.currentToken.deepType)) - { - const cacheToken = this.currentToken; - this.advance(); - - node = new UnaryOperationNode(cacheToken, this.makeFactor()); - } - else - { - node = this.makePower(); - } + const cacheToken = this.currentToken; + this.advance(); + + node = new UnaryOperationNode(cacheToken, this.makeFactor()); + } + else + { + node = this.makePower(); } if (!node) throw new ParserError(ParserErrors.TOKEN_EXPECTED, [...FACTORS[0]].toString()); @@ -189,7 +312,7 @@ export class Parser { let node: ParserNode = this.makeFactor(); - while (this.currentToken && TERMS.includes(this.currentToken.deepType)) + while (TERMS.includes(this.currentType)) { const operator: Token = this.currentToken; this.advance(); @@ -201,18 +324,105 @@ export class Parser return node; } + private makeArithmeticExpression(): ParserNode + { + let node: ParserNode = this.makeTerm(); + + while ([SymbolType.PLUS, SymbolType.MINUS].includes(this.currentType)) + { + const operator: Token = this.currentToken; + this.advance(); + + const right: ParserNode = this.makeTerm(); + node = new BinaryOperationNode(node, operator, right); + } + + return node; + } + + private makeComparisonExpression(): ParserNode + { + if (this.currentType === SymbolType.EXCLAMATION) + { + const operation: Token = this.currentToken; + this.advance(); + + const node: ParserNode = this.makeComparisonExpression(); + + return new UnaryOperationNode(operation, node); + } + + let node: ParserNode = this.makeArithmeticExpression(); + + while ([SymbolType.EQUALS, SymbolType.NOT_EQUAL, SymbolType.MORE_THAN, SymbolType.LESS_THAN, SymbolType.MORE_OR_EQUALS_TO, SymbolType.LESS_OR_EQUALS_TO].includes(this.currentType)) + { + const operator: Token = this.currentToken; + this.advance(); + + const right: ParserNode = this.makeArithmeticExpression(); + node = new BinaryOperationNode(node, operator, right); + } + + return node; + } + private makeExpression(): ParserNode { - if (this.currentToken && EXPRESSIONS[0].includes(this.currentToken.deepType)) + if (this.currentType === KeywordType.FUNCTION) + { + this.advance(); + + if (this.currentType !== TokenType.IDENTIFIER) throw new ParserError(ParserErrors.TOKEN_EXPECTED, TokenType.IDENTIFIER); + + const identifier: Token = this.currentToken; + this.advance(); + + if (this.currentType !== SymbolType.LEFT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.LEFT_PARENTHESES); + + this.advance(); + + const parameters: Token[] = []; + + if (this.currentType === TokenType.IDENTIFIER) + { + parameters.push(this.currentToken); + this.advance(); + + while (this.currentType === SymbolType.COMMA) + { + this.advance(); + + if (this.currentType !== TokenType.IDENTIFIER) throw new ParserError(ParserErrors.TOKEN_EXPECTED, TokenType.IDENTIFIER); + + parameters.push(this.currentToken); + + this.advance(); + } + } + + if (this.currentType !== SymbolType.RIGHT_PARENTHESES) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_PARENTHESES); + + this.advance(); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + + const body: ParserNode = this.makeCodeBlock(); + + while (this.currentType === TokenType.END_OF_LINE) this.advance(); + + return new FunctionAssignementNode(identifier, parameters, body); + } + + if (EXPRESSIONS[0].includes(this.currentType)) { this.advance(); - if (!this.currentToken || this.currentToken.deepType !== TokenType.IDENTIFIER) throw new ParserError(ParserErrors.TOKEN_EXPECTED, TokenType.IDENTIFIER); + if (this.currentType !== TokenType.IDENTIFIER) throw new ParserError(ParserErrors.TOKEN_EXPECTED, TokenType.IDENTIFIER); const identifier: Token = this.currentToken; this.advance(); - if (!this.currentToken || this.currentToken.deepType !== SymbolType.ATTRIBUTION) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.ATTRIBUTION); + if (this.currentType !== SymbolType.ATTRIBUTION) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.ATTRIBUTION); this.advance(); @@ -221,50 +431,43 @@ export class Parser return new VariableAssignementNode(identifier, expression); } - let node: ParserNode = this.makeTerm(); + let node: ParserNode = this.makeComparisonExpression(); - while (this.currentToken && EXPRESSIONS[1].includes(this.currentToken.deepType)) + while (EXPRESSIONS[1].includes(this.currentType)) { const operator: Token = this.currentToken; this.advance(); - const rightTerm: ParserNode = this.makeTerm(); - node = new BinaryOperationNode(node, operator, rightTerm); + const right: ParserNode = this.makeComparisonExpression(); + node = new BinaryOperationNode(node, operator, right); } return node; - } - public makeStatement(): ParserNode + public makeCodeBlock(): ParserNode { - const expression: ParserNode = this.makeExpression(); + const nodes: ParserNode[] = []; - if (!this.currentToken || ![TokenType.END_OF_LINE, TokenType.END_OF_FILE].includes(this.currentToken.deepType)) - { - throw new ParserError(ParserErrors.TOKEN_EXPECTED, [TokenType.END_OF_LINE, TokenType.END_OF_FILE].toString()); - } - - return expression; - } + if (this.currentType !== SymbolType.LEFT_BRACE) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.LEFT_BRACE); - public makeProgram(): ParserNode[] - { - const nodes: ParserNode[] = []; + this.advance(); - while(this.currentToken) + while(this.currentType !== SymbolType.RIGHT_BRACE) { - if (this.currentToken.deepType === TokenType.END_OF_FILE) break; - - if (this.currentToken.deepType === TokenType.END_OF_LINE) + if (this.currentType === TokenType.END_OF_LINE) { this.advance(); continue; } - nodes.push(this.makeStatement()); + nodes.push(this.makeExpression()); } - return nodes; + if (this.currentType !== SymbolType.RIGHT_BRACE) throw new ParserError(ParserErrors.TOKEN_EXPECTED, SymbolType.RIGHT_BRACE); + + this.advance(); + + return new CodeBlockNode(nodes); } } \ No newline at end of file diff --git a/src/parser/nodes/BinaryOperationNode.ts b/src/parser/nodes/BinaryOperationNode.ts index 09a82d8..5a9019f 100644 --- a/src/parser/nodes/BinaryOperationNode.ts +++ b/src/parser/nodes/BinaryOperationNode.ts @@ -17,6 +17,6 @@ export class BinaryOperationNode extends ParserNode public get representation(): string { - return `BinaryOperation: (${this.leftNode.representation}, ${this.operationToken.value}, ${this.rightNode.representation})`; + return `BinaryOperation: (${this.leftNode.representation}, ${this.operationToken.value}, ${this.rightNode.representation})\n`; } } \ No newline at end of file diff --git a/src/parser/nodes/CallingNode.ts b/src/parser/nodes/CallingNode.ts index 3120851..c116fe1 100644 --- a/src/parser/nodes/CallingNode.ts +++ b/src/parser/nodes/CallingNode.ts @@ -18,6 +18,6 @@ export class CallingNode extends ParserNode for(const argument of this.argumentsNodes) argumentsReps.push(argument.representation); - return `Call: ${this.identifierNode.representation}(${argumentsReps.join(',')})`; + return `Call: ${this.identifierNode.representation}(${argumentsReps.join(',')})\n`; } } \ No newline at end of file diff --git a/src/parser/nodes/CodeBlockNode.ts b/src/parser/nodes/CodeBlockNode.ts new file mode 100644 index 0000000..4579fea --- /dev/null +++ b/src/parser/nodes/CodeBlockNode.ts @@ -0,0 +1,21 @@ +import { ParserNode } from "./ParserNode"; + +export class CodeBlockNode extends ParserNode +{ + expressions: ParserNode[]; + + constructor(expressions: ParserNode[]) + { + super(); + this.expressions = expressions; + } + + public get representation(): string + { + const expressionsReps: string[] = []; + + for (const expression of this.expressions) expressionsReps.push(expression.representation); + + return `CodeBlock: (${expressionsReps.join(',')})\n`; + } +} \ No newline at end of file diff --git a/src/parser/nodes/ForNode.ts b/src/parser/nodes/ForNode.ts new file mode 100644 index 0000000..ab05103 --- /dev/null +++ b/src/parser/nodes/ForNode.ts @@ -0,0 +1,25 @@ +import { Token } from "../../lexer/Token"; +import { ParserNode } from "./ParserNode"; + +export class ForNode +{ + identifier: Token; + initialValue: ParserNode; + targetValue: ParserNode; + step: ParserNode; + body: ParserNode; + + constructor(identifier: Token, initialValue: ParserNode, targetValue: ParserNode, step: ParserNode, body: ParserNode) + { + this.identifier = identifier; + this.initialValue = initialValue; + this.targetValue = targetValue; + this.step = step; + this.body = body; + } + + public get representation(): string + { + return `For: (Variable: ${this.identifier.value}, Params: (${this.initialValue.representation} TO ${this.targetValue.representation} STEP ${this.step.representation}), Body: (${this.body.representation}))\n`; + } +} \ No newline at end of file diff --git a/src/parser/nodes/FunctionAssignementNode.ts b/src/parser/nodes/FunctionAssignementNode.ts new file mode 100644 index 0000000..e638bb0 --- /dev/null +++ b/src/parser/nodes/FunctionAssignementNode.ts @@ -0,0 +1,26 @@ +import { Token } from "../../lexer/Token"; +import { ParserNode } from "./ParserNode"; + +export class FunctionAssignementNode extends ParserNode +{ + variableToken: Token; + parametersTokens: Token[]; + body: ParserNode; + + constructor(variableToken: Token, parametersTokens: Token[], body: ParserNode) + { + super(); + this.variableToken = variableToken; + this.parametersTokens = parametersTokens; + this.body = body; + } + + public get representation(): string + { + var paramsReps: string[] = []; + + for(const parameter of this.parametersTokens) paramsReps.push(parameter.value); + + return `FunctionAssignement: (${this.variableToken.value}, Parameters: (${paramsReps.join(',')}), Body: (${this.body.representation}))\n`; + } +} \ No newline at end of file diff --git a/src/parser/nodes/IfCase.ts b/src/parser/nodes/IfCase.ts new file mode 100644 index 0000000..1538ec4 --- /dev/null +++ b/src/parser/nodes/IfCase.ts @@ -0,0 +1,18 @@ +import { ParserNode } from "./ParserNode"; + +export class IfCase +{ + conditionNode: ParserNode; + expressionNode: ParserNode; + + constructor(conditionNode: ParserNode, expressionNode: ParserNode) + { + this.conditionNode = conditionNode; + this.expressionNode = expressionNode; + } + + public get representation(): string + { + return `Case: Condition: (${this.conditionNode.representation}), Expression: (${this.expressionNode.representation})\n`; + } +} \ No newline at end of file diff --git a/src/parser/nodes/IfNode.ts b/src/parser/nodes/IfNode.ts new file mode 100644 index 0000000..a456a79 --- /dev/null +++ b/src/parser/nodes/IfNode.ts @@ -0,0 +1,24 @@ +import { IfCase } from './IfCase'; +import { ParserNode } from "./ParserNode"; + +export class IfNode extends ParserNode +{ + cases: IfCase[]; + elseCase: ParserNode | null; + + constructor(cases: IfCase[], elseCase: ParserNode | null) + { + super(); + this.cases = cases; + this.elseCase = elseCase; + } + + public get representation(): string + { + var casesReps: string[] = []; + + for(const casex of this.cases) casesReps.push(casex.representation); + + return `If: (${casesReps.join(',')}, Else: (${this.elseCase ? this.elseCase.representation : 'None'}))\n`; + } +} \ No newline at end of file diff --git a/src/parser/nodes/NumberNode.ts b/src/parser/nodes/NumberNode.ts index 2fef4aa..719fb2b 100644 --- a/src/parser/nodes/NumberNode.ts +++ b/src/parser/nodes/NumberNode.ts @@ -13,6 +13,6 @@ export class NumberNode extends ParserNode public get representation(): string { - return `Number: ${this.token.value}`; + return `Number: ${this.token.value}\n`; } } \ No newline at end of file diff --git a/src/parser/nodes/StringNode.ts b/src/parser/nodes/StringNode.ts index f3c49a4..031e519 100644 --- a/src/parser/nodes/StringNode.ts +++ b/src/parser/nodes/StringNode.ts @@ -13,6 +13,6 @@ export class StringNode extends ParserNode public get representation(): string { - return `String: "${this.token.value}"`; + return `String: "${this.token.value}"\n`; } } \ No newline at end of file diff --git a/src/parser/nodes/UnaryOperationNode.ts b/src/parser/nodes/UnaryOperationNode.ts index 70990b2..e4e9c49 100644 --- a/src/parser/nodes/UnaryOperationNode.ts +++ b/src/parser/nodes/UnaryOperationNode.ts @@ -15,6 +15,6 @@ export class UnaryOperationNode extends ParserNode public get representation(): string { - return `UnaryOperation: (${this.operationToken.value}, ${this.node.representation})`; + return `UnaryOperation: (${this.operationToken.value}, ${this.node.representation})\n`; } } \ No newline at end of file diff --git a/src/parser/nodes/VariableAccessNode.ts b/src/parser/nodes/VariableAccessNode.ts index 0d07193..9e67e8f 100644 --- a/src/parser/nodes/VariableAccessNode.ts +++ b/src/parser/nodes/VariableAccessNode.ts @@ -13,6 +13,6 @@ export class VariableAccessNode extends ParserNode public get representation(): string { - return `VariableAccess: ${this.variableToken.value}`; + return `VariableAccess: ${this.variableToken.value}\n`; } } \ No newline at end of file diff --git a/src/parser/nodes/VariableAssignementNode.ts b/src/parser/nodes/VariableAssignementNode.ts index 8b46b74..af2bc2e 100644 --- a/src/parser/nodes/VariableAssignementNode.ts +++ b/src/parser/nodes/VariableAssignementNode.ts @@ -15,6 +15,6 @@ export class VariableAssignementNode extends ParserNode public get representation(): string { - return `VariableAssignement: (${this.variableToken.value}, ${this.node.representation})`; + return `VariableAssignement: (${this.variableToken.value}, ${this.node.representation})\n`; } } \ No newline at end of file diff --git a/src/parser/nodes/WhileNode.ts b/src/parser/nodes/WhileNode.ts new file mode 100644 index 0000000..b8ac203 --- /dev/null +++ b/src/parser/nodes/WhileNode.ts @@ -0,0 +1,18 @@ +import { ParserNode } from "./ParserNode"; + +export class WhileNode +{ + conditionNode: ParserNode; + body: ParserNode; + + constructor(conditionNode: ParserNode, body: ParserNode) + { + this.conditionNode = conditionNode; + this.body = body; + } + + public get representation(): string + { + return `While: (Condition: (${this.conditionNode.representation}), Body: (${this.body.representation}))\n`; + } +} \ No newline at end of file From f0a79624c4bbbcf551b9c17d120b01ab4139a522 Mon Sep 17 00:00:00 2001 From: MyNameIsBatman Date: Tue, 31 May 2022 18:20:54 -0300 Subject: [PATCH 6/7] Remove old files --- src/_examples/teste.txt | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 src/_examples/teste.txt diff --git a/src/_examples/teste.txt b/src/_examples/teste.txt deleted file mode 100644 index 6aca5c7..0000000 --- a/src/_examples/teste.txt +++ /dev/null @@ -1,9 +0,0 @@ -{ - //Primeiro comentário awuiiiii - const abc= 1345.65;var vfd="meu email é 1800935@escolas.anchieta.br"; - if(abc === 1) abc++; - else abc--; - const finalSum = abc*2; - - //Segundo comentário aqui!!!! -} \ No newline at end of file From 489ed1e1bbd86ea112ee6aeddd9d38d1c865b360 Mon Sep 17 00:00:00 2001 From: MyNameIsBatman Date: Tue, 31 May 2022 18:21:57 -0300 Subject: [PATCH 7/7] Add title log --- src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/index.ts b/src/index.ts index c8782e6..778f5b8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,11 +15,13 @@ fs.readFile(filePath, {encoding: 'utf-8'}, (err, data) => { const lexer: Lexer = new Lexer(data); const tokens: Token[] = lexer.findTokens(); + console.log('Análise Léxica:'); console.table(tokens); const parser: Parser = new Parser(tokens); const codeBlock: ParserNode = parser.makeCodeBlock(); + console.log('Análise Sintática:'); if (codeBlock != null) console.log(codeBlock.representation); } else { console.error(err);