From be169ae9befa0724634818fdbbe670c58d30ec40 Mon Sep 17 00:00:00 2001 From: liulinboyi <814921718@qq.com> Date: Fri, 26 Feb 2021 23:56:24 +0800 Subject: [PATCH] fix: BinaryExpression error --- demo/hello-world-2.pineapple | 8 +-- demo/hello-world-3.pineapple | 4 ++ demo/hello-world-4.pineapple | 12 ++++ dist/src/lexer1.js | 82 +++++++++++------------ dist/src/parser.js | 53 ++++++++------- dist/src/parser/Assignment.js | 108 ++++++++++++++++++++++++------ dist/src/parser/Comment.js | 7 +- dist/src/parser/Print.js | 14 ++-- dist/test/test.js | 2 +- src/lexer1.ts | 20 +++++- src/parser.ts | 28 ++------ src/parser/Assignment.ts | 119 ++++++++++++++++++++++++++++++---- src/parser/Comment.ts | 3 +- src/parser/Print.ts | 4 +- test/test.js | 2 +- 15 files changed, 318 insertions(+), 148 deletions(-) create mode 100644 demo/hello-world-3.pineapple create mode 100644 demo/hello-world-4.pineapple diff --git a/demo/hello-world-2.pineapple b/demo/hello-world-2.pineapple index 6167c05..2555455 100644 --- a/demo/hello-world-2.pineapple +++ b/demo/hello-world-2.pineapple @@ -1,6 +1,4 @@ -#$a = 0 $b = 1 -#print($b) -#$e = "啊哈哈哈哈" -$c = $b -print($c) \ No newline at end of file +$d = $b - 1 +print($b) +print($d) diff --git a/demo/hello-world-3.pineapple b/demo/hello-world-3.pineapple new file mode 100644 index 0000000..b81249b --- /dev/null +++ b/demo/hello-world-3.pineapple @@ -0,0 +1,4 @@ +$b = 1 +$d = $b - $b +print($b) +print($d) diff --git a/demo/hello-world-4.pineapple b/demo/hello-world-4.pineapple new file mode 100644 index 0000000..5ef5f10 --- /dev/null +++ b/demo/hello-world-4.pineapple @@ -0,0 +1,12 @@ +$a = 10 +$b = $a- 5 +$d = 3 -2 +$c = 25 - $a +$e = 100 * 100 +$f = 1/2 +print($a) +print($b) +print($d) +print($c) +print($e) +print($f) diff --git a/dist/src/lexer1.js b/dist/src/lexer1.js index 03b1056..9de2b15 100644 --- a/dist/src/lexer1.js +++ b/dist/src/lexer1.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.NewLexer = exports.Lexer = exports.tokenNameMap = exports.keywords = exports.Tokens = void 0; +exports.NewLexer = exports.Lexer = exports.tokenNameMap = exports.keywords = exports.Operator = exports.SourceCharacter = exports.COMMENT = exports.STRING = exports.NUMBER = exports.INTERGER = exports.TOKEN_IGNORED = exports.TOKEN_PRINT = exports.TOKEN_NAME = exports.TOKEN_DUOQUOTE = exports.TOKEN_QUOTE = exports.TOKEN_EQUAL = exports.TOKEN_RIGHT_PAREN = exports.TOKEN_LEFT_PAREN = exports.TOKEN_VAR_PREFIX = exports.TOKEN_EOF = exports.Tokens = void 0; // token const var Tokens; (function (Tokens) { @@ -19,44 +19,32 @@ var Tokens; Tokens[Tokens["STRING"] = 12] = "STRING"; Tokens[Tokens["COMMENT"] = 13] = "COMMENT"; Tokens[Tokens["SourceCharacter"] = 14] = "SourceCharacter"; + Tokens[Tokens["Operator"] = 15] = "Operator"; })(Tokens = exports.Tokens || (exports.Tokens = {})); -const { TOKEN_EOF, // end-of-file -TOKEN_VAR_PREFIX, // $ -TOKEN_LEFT_PAREN, // ( -TOKEN_RIGHT_PAREN, // ) -TOKEN_EQUAL, // = -TOKEN_QUOTE, // " -TOKEN_DUOQUOTE, // "" -TOKEN_NAME, // Name ::= [_A-Za-z][_0-9A-Za-z]* -TOKEN_PRINT, // print -TOKEN_IGNORED, // Ignored -INTERGER, // [0-9]+ -NUMBER, // Integer Ignored -STRING, // String ::= '"' '"' Ignored | '"' StringCharacter '"' Ignored -COMMENT, // Ignored "#" SourceCharacter Ignored -SourceCharacter, } = Tokens; +exports.TOKEN_EOF = Tokens.TOKEN_EOF, exports.TOKEN_VAR_PREFIX = Tokens.TOKEN_VAR_PREFIX, exports.TOKEN_LEFT_PAREN = Tokens.TOKEN_LEFT_PAREN, exports.TOKEN_RIGHT_PAREN = Tokens.TOKEN_RIGHT_PAREN, exports.TOKEN_EQUAL = Tokens.TOKEN_EQUAL, exports.TOKEN_QUOTE = Tokens.TOKEN_QUOTE, exports.TOKEN_DUOQUOTE = Tokens.TOKEN_DUOQUOTE, exports.TOKEN_NAME = Tokens.TOKEN_NAME, exports.TOKEN_PRINT = Tokens.TOKEN_PRINT, exports.TOKEN_IGNORED = Tokens.TOKEN_IGNORED, exports.INTERGER = Tokens.INTERGER, exports.NUMBER = Tokens.NUMBER, exports.STRING = Tokens.STRING, exports.COMMENT = Tokens.COMMENT, exports.SourceCharacter = Tokens.SourceCharacter, exports.Operator = Tokens.Operator; // regex match patterns const regexName = /^[_\d\w]+/; // 关键字 exports.keywords = { - "print": TOKEN_PRINT, + "print": exports.TOKEN_PRINT, }; exports.tokenNameMap = { - [TOKEN_EOF]: "EOF", - [TOKEN_VAR_PREFIX]: "$", - [TOKEN_LEFT_PAREN]: "(", - [TOKEN_RIGHT_PAREN]: ")", - [TOKEN_EQUAL]: "=", - [TOKEN_QUOTE]: "\"", - [TOKEN_DUOQUOTE]: "\"\"", - [TOKEN_NAME]: "Name", - [TOKEN_PRINT]: "print", - [TOKEN_IGNORED]: "Ignored", - [INTERGER]: "INTERGER", - [NUMBER]: "NUMBER", - [STRING]: "STRING", - [COMMENT]: "COMMENT", - [SourceCharacter]: "SourceCharacter", + [exports.TOKEN_EOF]: "EOF", + [exports.TOKEN_VAR_PREFIX]: "$", + [exports.TOKEN_LEFT_PAREN]: "(", + [exports.TOKEN_RIGHT_PAREN]: ")", + [exports.TOKEN_EQUAL]: "=", + [exports.TOKEN_QUOTE]: "\"", + [exports.TOKEN_DUOQUOTE]: "\"\"", + [exports.TOKEN_NAME]: "Name", + [exports.TOKEN_PRINT]: "print", + [exports.TOKEN_IGNORED]: "Ignored", + [exports.INTERGER]: "INTERGER", + [exports.NUMBER]: "NUMBER", + [exports.STRING]: "STRING", + [exports.COMMENT]: "COMMENT", + [exports.SourceCharacter]: "SourceCharacter", + [exports.Operator]: "Operator", }; class Lexer { constructor(sourceCode, lineNum, nextToken, nextTokenType, nextTokenLineNum) { @@ -146,11 +134,11 @@ class Lexer { // console.log(this.sourceCode[0], '当前Token') // check ignored if (this.isIgnored()) { - return { lineNum: this.lineNum, tokenType: TOKEN_IGNORED, token: "Ignored" }; + return { lineNum: this.lineNum, tokenType: exports.TOKEN_IGNORED, token: "Ignored" }; } // finish if (this.sourceCode.length == 0) { - return { lineNum: this.lineNum, tokenType: TOKEN_EOF, token: exports.tokenNameMap[TOKEN_EOF] }; + return { lineNum: this.lineNum, tokenType: exports.TOKEN_EOF, token: exports.tokenNameMap[exports.TOKEN_EOF] }; } // 如果nextTokenType是#,并且字符串能匹配上,则表示是源代码字符串 // if (this.sourceCode[0].match(/\*/)) { @@ -160,26 +148,32 @@ class Lexer { switch (this.sourceCode[0]) { case '$': this.skipSourceCode(1); - return { lineNum: this.lineNum, tokenType: TOKEN_VAR_PREFIX, token: "$" }; + return { lineNum: this.lineNum, tokenType: exports.TOKEN_VAR_PREFIX, token: "$" }; case '(': this.skipSourceCode(1); - return { lineNum: this.lineNum, tokenType: TOKEN_LEFT_PAREN, token: "(" }; + return { lineNum: this.lineNum, tokenType: exports.TOKEN_LEFT_PAREN, token: "(" }; case ')': this.skipSourceCode(1); - return { lineNum: this.lineNum, tokenType: TOKEN_RIGHT_PAREN, token: ")" }; + return { lineNum: this.lineNum, tokenType: exports.TOKEN_RIGHT_PAREN, token: ")" }; case '=': this.skipSourceCode(1); - return { lineNum: this.lineNum, tokenType: TOKEN_EQUAL, token: "=" }; + return { lineNum: this.lineNum, tokenType: exports.TOKEN_EQUAL, token: "=" }; case '"': if (this.nextSourceCodeIs("\"\"")) { this.skipSourceCode(2); - return { lineNum: this.lineNum, tokenType: TOKEN_DUOQUOTE, token: "\"\"" }; + return { lineNum: this.lineNum, tokenType: exports.TOKEN_DUOQUOTE, token: "\"\"" }; } this.skipSourceCode(1); - return { lineNum: this.lineNum, tokenType: TOKEN_QUOTE, token: "\"" }; + return { lineNum: this.lineNum, tokenType: exports.TOKEN_QUOTE, token: "\"" }; case '#': this.skipSourceCode(1); - return { lineNum: this.lineNum, tokenType: COMMENT, token: "#" }; + return { lineNum: this.lineNum, tokenType: exports.COMMENT, token: "#" }; + } + // Operator + if (/\+|\-|\*|\//.test(this.sourceCode[0])) { + const op = this.sourceCode[0]; + this.skipSourceCode(1); + return { lineNum: this.lineNum, tokenType: exports.Operator, token: op }; } // check multiple character token if (this.sourceCode[0] == '_' || this.isLetter(this.sourceCode[0])) { @@ -191,11 +185,13 @@ class Lexer { return { lineNum: this.lineNum, tokenType, token }; } else { - return { lineNum: this.lineNum, tokenType: TOKEN_NAME, token }; + return { lineNum: this.lineNum, tokenType: exports.TOKEN_NAME, token }; } } if (this.isNumber(this.sourceCode[0])) { - return { lineNum: this.lineNum, tokenType: NUMBER, token: this.sourceCode[0] }; + const num = this.sourceCode[0]; + this.skipSourceCode(1); + return { lineNum: this.lineNum, tokenType: exports.NUMBER, token: num }; } // unexpected symbol throw new Error(`MatchToken(): unexpected symbol near '${this.sourceCode[0]}'.`); diff --git a/dist/src/parser.js b/dist/src/parser.js index b2efc63..5d0425c 100644 --- a/dist/src/parser.js +++ b/dist/src/parser.js @@ -1,11 +1,10 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.parse = exports.parseString = exports.parseNumber = exports.parseVariable = exports.Program = exports.SourceCharacter = exports.COMMENT = exports.STRING = exports.NUMBER = exports.INTERGER = exports.TOKEN_IGNORED = exports.TOKEN_PRINT = exports.TOKEN_NAME = exports.TOKEN_DUOQUOTE = exports.TOKEN_QUOTE = exports.TOKEN_EQUAL = exports.TOKEN_RIGHT_PAREN = exports.TOKEN_LEFT_PAREN = exports.TOKEN_VAR_PREFIX = exports.TOKEN_EOF = void 0; +exports.parse = exports.parseString = exports.parseNumber = exports.parseVariable = exports.Program = void 0; const lexer1_1 = require("./lexer1"); const Comment_1 = require("./parser/Comment"); const Print_1 = require("./parser/Print"); const Assignment_1 = require("./parser/Assignment"); -exports.TOKEN_EOF = lexer1_1.Tokens.TOKEN_EOF, exports.TOKEN_VAR_PREFIX = lexer1_1.Tokens.TOKEN_VAR_PREFIX, exports.TOKEN_LEFT_PAREN = lexer1_1.Tokens.TOKEN_LEFT_PAREN, exports.TOKEN_RIGHT_PAREN = lexer1_1.Tokens.TOKEN_RIGHT_PAREN, exports.TOKEN_EQUAL = lexer1_1.Tokens.TOKEN_EQUAL, exports.TOKEN_QUOTE = lexer1_1.Tokens.TOKEN_QUOTE, exports.TOKEN_DUOQUOTE = lexer1_1.Tokens.TOKEN_DUOQUOTE, exports.TOKEN_NAME = lexer1_1.Tokens.TOKEN_NAME, exports.TOKEN_PRINT = lexer1_1.Tokens.TOKEN_PRINT, exports.TOKEN_IGNORED = lexer1_1.Tokens.TOKEN_IGNORED, exports.INTERGER = lexer1_1.Tokens.INTERGER, exports.NUMBER = lexer1_1.Tokens.NUMBER, exports.STRING = lexer1_1.Tokens.STRING, exports.COMMENT = lexer1_1.Tokens.COMMENT, exports.SourceCharacter = lexer1_1.Tokens.SourceCharacter; class Program { constructor(type, body, LineNum) { this.type = 'Program'; @@ -37,55 +36,55 @@ function parseStatements(lexer) { } function parseStatement(lexer) { // 向前看一个token并跳过 - lexer.LookAheadAndSkip(exports.TOKEN_IGNORED); // skip if source code start with ignored token + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); // skip if source code start with ignored token let look = lexer.LookAhead().tokenType; console.log(look, 'look'); switch (look) { - case exports.TOKEN_PRINT: + case lexer1_1.TOKEN_PRINT: return Print_1.parsePrint(lexer); - case exports.TOKEN_VAR_PREFIX: + case lexer1_1.TOKEN_VAR_PREFIX: return Assignment_1.parseAssignment(lexer); - case exports.COMMENT: + case lexer1_1.COMMENT: return Comment_1.paseComment(lexer); default: throw new Error("parseStatement(): unknown Statement."); } } function isSourceCodeEnd(token) { - return token === exports.TOKEN_EOF; + return token === lexer1_1.TOKEN_EOF; } // Variable ::= "$" Name Ignored function parseVariable(lexer) { let variable = { Name: '' }; variable.LineNum = lexer.GetLineNum(); - lexer.NextTokenIs(exports.TOKEN_VAR_PREFIX); + lexer.NextTokenIs(lexer1_1.TOKEN_VAR_PREFIX); variable.Name = parseName(lexer); - lexer.LookAheadAndSkip(exports.TOKEN_IGNORED); + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); return variable; } exports.parseVariable = parseVariable; // Name ::= [_A-Za-z][_0-9A-Za-z]* function parseName(lexer) { - let { nowLineNum: _, nowToken: name } = lexer.NextTokenIs(exports.TOKEN_NAME); + let { nowLineNum: _, nowToken: name } = lexer.NextTokenIs(lexer1_1.TOKEN_NAME); return name; } // Integer ::= [0-9]+ // Number ::= Integer Ignored function parseNumber(lexer) { let str = ""; - let { tokenType, token } = lexer.MatchToken(); + let { tokenType, token } = lexer.LookAhead(); str += token; // console.log(tokenType, 'parseNumber', str, 'str') - if (tokenType === exports.NUMBER) { + if (tokenType === lexer1_1.NUMBER) { while (lexer.isNumber(lexer.sourceCode[0])) { // console.log(lexer.sourceCode[0]) - str = lexer.next(1); + str += lexer.next(1); } - if (!lexer.isIgnored()) { - throw new Error('Uncaught SyntaxError: Invalid or unexpected token'); - } - lexer.NextTokenIs(exports.NUMBER); - lexer.LookAheadAndSkip(exports.TOKEN_IGNORED); + // if (!lexer.isIgnored()) { + // throw new Error('Uncaught SyntaxError: Invalid or unexpected token') + // } + lexer.NextTokenIs(lexer1_1.NUMBER); + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); } return +str; } @@ -95,15 +94,15 @@ function parseString(lexer) { let str = ""; let look = lexer.LookAhead().tokenType; switch (look) { - case exports.TOKEN_DUOQUOTE: - lexer.NextTokenIs(exports.TOKEN_DUOQUOTE); - lexer.LookAheadAndSkip(exports.TOKEN_IGNORED); + case lexer1_1.TOKEN_DUOQUOTE: + lexer.NextTokenIs(lexer1_1.TOKEN_DUOQUOTE); + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); return str; - case exports.TOKEN_QUOTE: - lexer.NextTokenIs(exports.TOKEN_QUOTE); // 这里已经吃掉一个单个双引号了 - str = lexer.scanBeforeToken(lexer1_1.tokenNameMap[exports.TOKEN_QUOTE]); // 这里就会成这样了 eg: aa"后面其他字符串 - lexer.NextTokenIs(exports.TOKEN_QUOTE); - lexer.LookAheadAndSkip(exports.TOKEN_IGNORED); + case lexer1_1.TOKEN_QUOTE: + lexer.NextTokenIs(lexer1_1.TOKEN_QUOTE); // 这里已经吃掉一个单个双引号了 + str = lexer.scanBeforeToken(lexer1_1.tokenNameMap[lexer1_1.TOKEN_QUOTE]); // 这里就会成这样了 eg: aa"后面其他字符串 + lexer.NextTokenIs(lexer1_1.TOKEN_QUOTE); + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); return str; default: return ""; @@ -113,7 +112,7 @@ exports.parseString = parseString; function parse(code) { let lexer = lexer1_1.NewLexer(code); let sourceCode = parseSourceCode(lexer); - lexer.NextTokenIs(exports.TOKEN_EOF); + lexer.NextTokenIs(lexer1_1.TOKEN_EOF); // console.log(JSON.stringify(sourceCode), 'sourceCode') return sourceCode; } diff --git a/dist/src/parser/Assignment.js b/dist/src/parser/Assignment.js index 06a6228..c0f4d23 100644 --- a/dist/src/parser/Assignment.js +++ b/dist/src/parser/Assignment.js @@ -1,6 +1,7 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.parseAssignment = exports.Assignment = exports.Literal = void 0; +exports.parseAssignment = exports.Assignment = exports.Identifier = exports.Literal = void 0; +const lexer1_1 = require("../lexer1"); const parser_1 = require("../parser"); class Literal { constructor(value, type = 'Literal') { @@ -9,6 +10,13 @@ class Literal { } } exports.Literal = Literal; +class Identifier { + constructor(name, type = "Identifier") { + this.name = name; + this.type = type; + } +} +exports.Identifier = Identifier; class Assignment { constructor(LineNum, Variable, String, num, type, Literal, kind) { this.LineNum = LineNum; @@ -30,41 +38,103 @@ function parseAssignment(lexer) { VariableDeclarator.id = { type: "Identifier" }; VariableDeclarator.id.name = parser_1.parseVariable(lexer).Name; // assignment.Variable = parseVariable(lexer) // 标识符 - lexer.LookAheadAndSkip(parser_1.TOKEN_IGNORED); // 空格 - lexer.NextTokenIs(parser_1.TOKEN_EQUAL); // = - lexer.LookAheadAndSkip(parser_1.TOKEN_IGNORED); // 空格 - console.log(lexer.LookAhead().tokenType, 'lexer.LookAhead().tokenType'); + // $a = "aaa" + // $a = 1 + // $a = $b + // $a = 1 - 1 + // $a = $b - 1 + // $a = $b - $c + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); // 空格 + lexer.NextTokenIs(lexer1_1.TOKEN_EQUAL); // = + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); // 空格 + const tokenType = lexer.LookAhead().tokenType; + console.log(tokenType, 'lexer.LookAhead().tokenType'); // 如果后面仍是$ - if (lexer.LookAhead().tokenType === parser_1.TOKEN_VAR_PREFIX) { - const Variable = parser_1.parseVariable(lexer); // 标识符 + if (tokenType === lexer1_1.TOKEN_VAR_PREFIX) { + const Variable = parser_1.parseVariable(lexer); // 标识符,这里面会把邻近的空格回车删掉 console.log(Variable, 'Variable'); - const identifier = { name: Variable.Name, type: "Identifier" }; + const identifier = new Identifier(Variable.Name); VariableDeclarator.init = identifier; assignment.type = "VariableDeclaration"; assignment.declarations.push(VariableDeclarator); // 一行只允许声明和初始化一个变量 - // assignment.Variable = Variable - return assignment; + let ahead = lexer.LookAhead(); + console.log(ahead, 'parseAssignment Variable ahead'); + if (ahead.tokenType !== lexer1_1.Operator) { + return assignment; + } + else { + lexer.NextTokenIs(lexer1_1.Operator); // +-*/ + // lexer.LookAheadAndSkip(TOKEN_IGNORED) // 空格 + lexer.isIgnored(); + const idAndinit = assignment.declarations.pop(); + return parseBinaryExpression(lexer, idAndinit, assignment, "Identifier"); + } } else { - if (lexer.isNumber(lexer.sourceCode[0])) { + if (tokenType === lexer1_1.NUMBER) { // console.log('parseNumber start') - const literial = new Literal(parser_1.parseNumber(lexer)); + const literial = new Literal(parser_1.parseNumber(lexer)); // 这里面会把邻近的空格回车删掉 VariableDeclarator.init = literial; - // assignment.Literal = literial - // assignment.type = tokenNameMap[NUMBER] assignment.type = "VariableDeclaration"; // console.log('parseNumber end') } else { - const literial = new Literal(parser_1.parseString(lexer)); - // assignment.Literal = literial + const literial = new Literal(parser_1.parseString(lexer)); // 这里面会把邻近的空格回车删掉 VariableDeclarator.init = literial; - // assignment.type = tokenNameMap[STRING] assignment.type = "VariableDeclaration"; } - lexer.LookAheadAndSkip(parser_1.TOKEN_IGNORED); assignment.declarations.push(VariableDeclarator); // 一行只允许声明和初始化一个变量 - return assignment; + let ahead = lexer.LookAhead(); + console.log(ahead, 'parseAssignment not Variable ahead'); + if (ahead.tokenType !== lexer1_1.Operator) { + return assignment; + } + else { + lexer.NextTokenIs(lexer1_1.Operator); // +-*/ + // lexer.LookAheadAndSkip(TOKEN_IGNORED); // 空格 + lexer.isIgnored(); + const idAndinit = assignment.declarations.pop(); + return parseBinaryExpression(lexer, idAndinit, assignment, "Literal"); + } } } exports.parseAssignment = parseAssignment; +function parseBinaryExpression(lexer, idAndinit, assignment, leftType) { + const BinaryExpression = { + type: "BinaryExpression", + left: { + // type: "Identifier", + // name: "c" + }, + operator: lexer.nextToken, + right: { + // type: "Identifier", + // name: "b" + } + }; + let ahead = lexer.LookAhead(); + console.log(ahead, 'parseBinaryExpression ahead'); + if (leftType === 'Identifier') { + BinaryExpression.left = new Identifier(idAndinit.init.name); + } + else if (leftType === 'Literal') { + BinaryExpression.left = new Literal(idAndinit.init.value); + } + if (ahead.tokenType === lexer1_1.NUMBER) { + console.log('NUMBER'); + const literial = new Literal(parser_1.parseNumber(lexer)); + BinaryExpression.right = literial; + } + else if (ahead.tokenType === lexer1_1.TOKEN_VAR_PREFIX) { + const Variable = parser_1.parseVariable(lexer); // 标识符 + console.log(Variable, 'Variable'); + const identifier = new Identifier(Variable.Name); + BinaryExpression.right = identifier; + } + let VariableDeclarator = { type: "VariableDeclarator" }; + VariableDeclarator.id = { type: "Identifier" }; + VariableDeclarator.id.name = idAndinit.id.name; + VariableDeclarator.init = BinaryExpression; + assignment.declarations.push(VariableDeclarator); + return assignment; +} diff --git a/dist/src/parser/Comment.js b/dist/src/parser/Comment.js index f37db60..9a4750a 100644 --- a/dist/src/parser/Comment.js +++ b/dist/src/parser/Comment.js @@ -2,7 +2,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.paseComment = exports.Comment = void 0; const lexer1_1 = require("../lexer1"); -const parser_1 = require("../parser"); class Comment { constructor(LineNum, Content, type) { this.LineNum = LineNum; @@ -15,16 +14,16 @@ function paseComment(lexer) { let comment = new Comment(); console.log("paseComment start"); comment.LineNum = lexer.GetLineNum(); - lexer.NextTokenIs(parser_1.COMMENT); + lexer.NextTokenIs(lexer1_1.COMMENT); console.log(lexer.isNewLine(lexer.sourceCode[0]), 'isNewLine'); let content = ""; // 如果换行或者源码为空则停止解析注释 while (!lexer.isNewLine(lexer.sourceCode[0]) && !lexer.isEmpty()) { content += lexer.next(1); } - lexer.LookAheadAndSkip(parser_1.TOKEN_IGNORED); + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); comment.Content = content; - comment.type = lexer1_1.tokenNameMap[parser_1.COMMENT]; + comment.type = lexer1_1.tokenNameMap[lexer1_1.COMMENT]; return comment; } exports.paseComment = paseComment; diff --git a/dist/src/parser/Print.js b/dist/src/parser/Print.js index c966eca..765a909 100644 --- a/dist/src/parser/Print.js +++ b/dist/src/parser/Print.js @@ -15,14 +15,14 @@ exports.Print = Print; function parsePrint(lexer) { let print = new Print(); print.LineNum = lexer.GetLineNum(); - lexer.NextTokenIs(parser_1.TOKEN_PRINT); - lexer.NextTokenIs(parser_1.TOKEN_LEFT_PAREN); - lexer.LookAheadAndSkip(parser_1.TOKEN_IGNORED); + lexer.NextTokenIs(lexer1_1.TOKEN_PRINT); + lexer.NextTokenIs(lexer1_1.TOKEN_LEFT_PAREN); + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); print.Variable = parser_1.parseVariable(lexer); - print.type = lexer1_1.tokenNameMap[parser_1.TOKEN_PRINT]; - lexer.LookAheadAndSkip(parser_1.TOKEN_IGNORED); - lexer.NextTokenIs(parser_1.TOKEN_RIGHT_PAREN); - lexer.LookAheadAndSkip(parser_1.TOKEN_IGNORED); + print.type = lexer1_1.tokenNameMap[lexer1_1.TOKEN_PRINT]; + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); + lexer.NextTokenIs(lexer1_1.TOKEN_RIGHT_PAREN); + lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); const p = { "type": "ExpressionStatement", "expression": { diff --git a/dist/test/test.js b/dist/test/test.js index f11e9cc..3e944d0 100644 --- a/dist/test/test.js +++ b/dist/test/test.js @@ -2,7 +2,7 @@ const fs = require('fs'); const path = require('path'); const Execute = require('../dist/src/backend.js').Execute; -code = fs.readFileSync(path.resolve(__dirname, '../demo/hello-world-2.pineapple'), { encoding: 'utf-8' }); +code = fs.readFileSync(path.resolve(__dirname, '../demo/hello-world-4.pineapple'), { encoding: 'utf-8' }); console.log(code, 'code'); if (code.length > 0) { Execute(code); diff --git a/src/lexer1.ts b/src/lexer1.ts index f70ef5b..0b8be31 100644 --- a/src/lexer1.ts +++ b/src/lexer1.ts @@ -8,11 +8,14 @@ Integer ::= [0-9]+ Number ::= Integer Ignored String ::= '"' '"' Ignored | '"' StringCharacter '"' Ignored Variable ::= "$" Name Ignored // 变量 -Assignment ::= Variable Ignored '=' Ignored ( String | Number | Variable) Ignored +Assignment ::= Variable Ignored '=' Ignored ( String | Number | Variable | BinaryExpression) Ignored Print ::= "print" "(" Ignored Variable Ignored ")" Ignored Statement ::= Print | Assignment SourceCode ::= Statement+ Comment ::= Ignored "#" SourceCharacter // 注释 +BinaryExpression::= (Variable | Number) Ignored Operator Ignored (Variable | Number) +Operator ::= "+" | "-" | "*" | "/" +BinaryExpressions ::= (BinaryExpression Operator)+ Ignored (Variable | Number) // eg: 1: (2 + 1 +) 3 2: ((2 + 1 +) (5 + 6 -)) 3 */ @@ -43,9 +46,10 @@ export enum Tokens { STRING, // String ::= '"' '"' Ignored | '"' StringCharacter '"' Ignored COMMENT, // Ignored "#" SourceCharacter Ignored SourceCharacter, // 所有代码字符串 + Operator, // +-*/ Operator } -const { TOKEN_EOF, // end-of-file +export const { TOKEN_EOF, // end-of-file TOKEN_VAR_PREFIX, // $ TOKEN_LEFT_PAREN, // ( TOKEN_RIGHT_PAREN, // ) @@ -60,6 +64,7 @@ const { TOKEN_EOF, // end-of-file STRING, // String ::= '"' '"' Ignored | '"' StringCharacter '"' Ignored COMMENT, // Ignored "#" SourceCharacter Ignored SourceCharacter, // 所有代码字符串 + Operator, // +-*/ Operator } = Tokens // regex match patterns @@ -86,6 +91,7 @@ export const tokenNameMap: TokenNameMap = { [STRING]: "STRING", [COMMENT]: "COMMENT", [SourceCharacter]: "SourceCharacter", + [Operator]: "Operator", } export class Lexer { @@ -215,6 +221,12 @@ export class Lexer { this.skipSourceCode(1) return { lineNum: this.lineNum, tokenType: COMMENT, token: "#" } } + // Operator + if (/\+|\-|\*|\//.test(this.sourceCode[0])) { + const op = this.sourceCode[0] + this.skipSourceCode(1) + return { lineNum: this.lineNum, tokenType: Operator, token: op } + } // check multiple character token if (this.sourceCode[0] == '_' || this.isLetter(this.sourceCode[0])) { // 扫描关键字 @@ -228,7 +240,9 @@ export class Lexer { } } if (this.isNumber(this.sourceCode[0])) { - return { lineNum: this.lineNum, tokenType: NUMBER, token: this.sourceCode[0] } + const num = this.sourceCode[0] + this.skipSourceCode(1) + return { lineNum: this.lineNum, tokenType: NUMBER, token: num } } // unexpected symbol throw new Error(`MatchToken(): unexpected symbol near '${this.sourceCode[0]}'.`); diff --git a/src/parser.ts b/src/parser.ts index c196369..fb01cf4 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1,25 +1,9 @@ -import { Lexer, NewLexer, tokenNameMap, Tokens } from "./lexer1" +import { COMMENT, Lexer, NewLexer, NUMBER, tokenNameMap, TOKEN_DUOQUOTE, TOKEN_EOF, TOKEN_IGNORED, TOKEN_NAME, TOKEN_PRINT, TOKEN_QUOTE, TOKEN_VAR_PREFIX } from "./lexer1" import { Variable } from './definition' import { paseComment } from './parser/Comment' import { parsePrint, Print } from "./parser/Print" import { Assignment, parseAssignment } from "./parser/Assignment" -export const { TOKEN_EOF, // end-of-file - TOKEN_VAR_PREFIX, // $ - TOKEN_LEFT_PAREN, // ( - TOKEN_RIGHT_PAREN, // ) - TOKEN_EQUAL, // = - TOKEN_QUOTE, // " - TOKEN_DUOQUOTE, // "" - TOKEN_NAME, // Name ::= [_A-Za-z][_0-9A-Za-z]* - TOKEN_PRINT, // print - TOKEN_IGNORED, // Ignored - INTERGER, // [0-9]+ - NUMBER, // Integer Ignored - STRING, // String ::= '"' '"' Ignored | '"' StringCharacter '"' Ignored - COMMENT, // Ignored "#" SourceCharacter Ignored - SourceCharacter, // 所有代码字符串 -} = Tokens export class Program { constructor(type?: string, body?: Array, LineNum?: number) { @@ -107,18 +91,18 @@ function parseName(lexer: Lexer) { // Number ::= Integer Ignored export function parseNumber(lexer: Lexer) { let str = "" - let { tokenType, token } = lexer.MatchToken() + let { tokenType, token } = lexer.LookAhead() str += token // console.log(tokenType, 'parseNumber', str, 'str') if (tokenType === NUMBER) { while (lexer.isNumber(lexer.sourceCode[0])) { // console.log(lexer.sourceCode[0]) - str = lexer.next(1) - } - if (!lexer.isIgnored()) { - throw new Error('Uncaught SyntaxError: Invalid or unexpected token') + str += lexer.next(1) } + // if (!lexer.isIgnored()) { + // throw new Error('Uncaught SyntaxError: Invalid or unexpected token') + // } lexer.NextTokenIs(NUMBER) lexer.LookAheadAndSkip(TOKEN_IGNORED) diff --git a/src/parser/Assignment.ts b/src/parser/Assignment.ts index 03c0d95..839270c 100644 --- a/src/parser/Assignment.ts +++ b/src/parser/Assignment.ts @@ -1,6 +1,6 @@ import { Variable } from "../definition"; -import { Lexer, tokenNameMap, Tokens } from "../lexer1"; -import { NUMBER, parseNumber, parseString, parseVariable, STRING, TOKEN_EQUAL, TOKEN_IGNORED, TOKEN_VAR_PREFIX } from "../parser"; +import { Lexer, NUMBER, Operator, tokenNameMap, Tokens, TOKEN_EQUAL, TOKEN_IGNORED, TOKEN_VAR_PREFIX } from "../lexer1"; +import { parseNumber, parseString, parseVariable } from "../parser"; export interface Assignment { LineNum?: number, @@ -17,6 +17,11 @@ export interface Literal { value: string | number } +export interface Identifier { + name: string, + type?: string, +} + export class Literal { constructor(value: string | number, type = 'Literal') { this.type = type @@ -24,6 +29,13 @@ export class Literal { } } +export class Identifier { + constructor(name: string, type = "Identifier") { + this.name = name + this.type = type + } +} + export class Assignment { constructor(LineNum?: number, Variable?: Variable, String?: string, num?: number, type?: string, Literal?: Literal, kind?: string) { this.LineNum = LineNum @@ -47,36 +59,119 @@ export function parseAssignment(lexer: Lexer) { VariableDeclarator.id = { type: "Identifier" } VariableDeclarator.id.name = parseVariable(lexer).Name // assignment.Variable = parseVariable(lexer) // 标识符 + // $a = "aaa" + // $a = 1 + // $a = $b + // $a = 1 - 1 + // $a = $b - 1 + // $a = $b - $c lexer.LookAheadAndSkip(TOKEN_IGNORED) // 空格 lexer.NextTokenIs(TOKEN_EQUAL) // = lexer.LookAheadAndSkip(TOKEN_IGNORED) // 空格 - console.log(lexer.LookAhead().tokenType, 'lexer.LookAhead().tokenType') + const tokenType = lexer.LookAhead().tokenType + + console.log(tokenType, 'lexer.LookAhead().tokenType') // 如果后面仍是$ - if (lexer.LookAhead().tokenType === TOKEN_VAR_PREFIX) { - const Variable = parseVariable(lexer) // 标识符 + if (tokenType === TOKEN_VAR_PREFIX) { + const Variable = parseVariable(lexer) // 标识符,这里面会把邻近的空格回车删掉 console.log(Variable, 'Variable') - const identifier = { name: Variable.Name, type: "Identifier" } + const identifier = new Identifier(Variable.Name); VariableDeclarator.init = identifier assignment.type = "VariableDeclaration" assignment.declarations.push(VariableDeclarator) // 一行只允许声明和初始化一个变量 - return assignment + + let ahead = lexer.LookAhead() + console.log(ahead, 'parseAssignment Variable ahead') + + if (ahead.tokenType !== Operator) { + return assignment + } else { + lexer.NextTokenIs(Operator) // +-*/ + // lexer.LookAheadAndSkip(TOKEN_IGNORED) // 空格 + lexer.isIgnored() + const idAndinit = assignment.declarations.pop() + return parseBinaryExpression(lexer, idAndinit, assignment, "Identifier") + } } else { - if (lexer.isNumber(lexer.sourceCode[0])) { + if (tokenType === NUMBER) { // console.log('parseNumber start') - const literial = new Literal(parseNumber(lexer)) + const literial = new Literal(parseNumber(lexer)) // 这里面会把邻近的空格回车删掉 VariableDeclarator.init = literial assignment.type = "VariableDeclaration" // console.log('parseNumber end') } else { - const literial = new Literal(parseString(lexer)) + const literial = new Literal(parseString(lexer)) // 这里面会把邻近的空格回车删掉 VariableDeclarator.init = literial assignment.type = "VariableDeclaration" } - lexer.LookAheadAndSkip(TOKEN_IGNORED) assignment.declarations.push(VariableDeclarator) // 一行只允许声明和初始化一个变量 - return assignment + + let ahead = lexer.LookAhead() + console.log(ahead, 'parseAssignment not Variable ahead') + + if (ahead.tokenType !== Operator) { + return assignment + } else { + lexer.NextTokenIs(Operator); // +-*/ + // lexer.LookAheadAndSkip(TOKEN_IGNORED); // 空格 + lexer.isIgnored() + const idAndinit = assignment.declarations.pop(); + return parseBinaryExpression(lexer, idAndinit, assignment, "Literal"); + } + } +} + +function parseBinaryExpression(lexer: Lexer, idAndinit: { init: Literal | Identifier, id: Identifier }, assignment: Assignment, leftType: string) { + const BinaryExpression: { + type: string, + left: { + type?: string, + name?: string, + value?: number | string + }, + operator: string, + right: { + type?: string, + name?: string, + } + } = { + type: "BinaryExpression", + left: { + // type: "Identifier", + // name: "c" + }, + operator: lexer.nextToken, + right: { + // type: "Identifier", + // name: "b" + } } + + let ahead = lexer.LookAhead() + console.log(ahead, 'parseBinaryExpression ahead') + if (leftType === 'Identifier') { + BinaryExpression.left = new Identifier((idAndinit.init as Identifier).name) + } else if (leftType === 'Literal') { + BinaryExpression.left = new Literal((idAndinit.init as Literal).value) + } + if (ahead.tokenType === NUMBER) { + console.log('NUMBER') + const literial = new Literal(parseNumber(lexer)) + BinaryExpression.right = literial + } else if (ahead.tokenType === TOKEN_VAR_PREFIX) { + const Variable = parseVariable(lexer) // 标识符 + console.log(Variable, 'Variable') + const identifier = new Identifier(Variable.Name); + BinaryExpression.right = identifier + } + + let VariableDeclarator: any = { type: "VariableDeclarator" } + VariableDeclarator.id = { type: "Identifier" }; + VariableDeclarator.id.name = idAndinit.id.name; + VariableDeclarator.init = BinaryExpression; + (assignment as any).declarations.push(VariableDeclarator) + return assignment } \ No newline at end of file diff --git a/src/parser/Comment.ts b/src/parser/Comment.ts index be23d0e..15459ae 100644 --- a/src/parser/Comment.ts +++ b/src/parser/Comment.ts @@ -1,5 +1,4 @@ -import { Lexer, tokenNameMap, Tokens } from "../lexer1" -import { COMMENT, SourceCharacter, TOKEN_IGNORED } from "../parser" +import { COMMENT, Lexer, tokenNameMap, Tokens, TOKEN_IGNORED } from "../lexer1" export interface Comment { LineNum?: number, diff --git a/src/parser/Print.ts b/src/parser/Print.ts index c6b9ad8..8bc60a4 100644 --- a/src/parser/Print.ts +++ b/src/parser/Print.ts @@ -1,6 +1,6 @@ import { Variable } from "../definition" -import { Lexer, tokenNameMap, Tokens } from "../lexer1" -import { parseVariable, TOKEN_IGNORED, TOKEN_LEFT_PAREN, TOKEN_PRINT, TOKEN_RIGHT_PAREN } from "../parser" +import { Lexer, tokenNameMap, Tokens, TOKEN_IGNORED, TOKEN_LEFT_PAREN, TOKEN_PRINT, TOKEN_RIGHT_PAREN } from "../lexer1" +import { parseVariable } from "../parser" export interface Print { LineNum?: number, diff --git a/test/test.js b/test/test.js index 2b794a9..c739328 100644 --- a/test/test.js +++ b/test/test.js @@ -2,7 +2,7 @@ const fs = require('fs') const path = require('path') const Execute = require('../dist/src/backend.js').Execute -code = fs.readFileSync(path.resolve(__dirname, '../demo/hello-world-2.pineapple'), {encoding: 'utf-8'}) +code = fs.readFileSync(path.resolve(__dirname, '../demo/hello-world-4.pineapple'), {encoding: 'utf-8'}) console.log(code, 'code') if (code.length > 0) { Execute(code)