Skip to content

Commit

Permalink
feat: Complete this stage
Browse files Browse the repository at this point in the history
  • Loading branch information
liulinboyi committed Feb 27, 2021
1 parent 186a121 commit 83bfacc
Show file tree
Hide file tree
Showing 24 changed files with 99 additions and 102 deletions.
36 changes: 33 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ BinaryExpression::= (Variable | Number) Ignored Operator Ignored (Variable | Num
Operator ::= "+" | "-" | "*" | "/" | ">" | "<" | "==" | ">=" | "<="
BinaryExpressions ::= (BinaryExpression Operator)+ Ignored (Variable | Number) // eg: 1: (2 + 1 +) 3 2: ((2 + 1 +) (5 + 6 -)) 3
FunctionDeclaration ::= "func" Ignored Name Ignored "(" Variable ("," Variable)* ")" BlockStatement // eg: 1: func foo ($a) {} 2: func foo ($a[,$b][,$c]) {} ("," Variable)*这部分是一个或多个
BlockStatement ::= "{" Ignored (IfStatement | Print | Assignment | ReturnStatement ) Ignored "}"
BlockStatement ::= "{" Ignored (IfStatement | CallFunction | Print | Assignment | ReturnStatement ) Ignored "}"
ReturnStatement ::= "return" (BinaryExpression | Variable)
CallFunction ::= Name "(" (Variable | Number) ("," (Variable | Number))* ")" Ignored
IfStatement ::= "if" Ignored "(" Variable Ignored Operator Ignored Variable ")" Ignored BlockStatement Ignored "else" Ignored BlockStatement Ignored
Expand All @@ -31,7 +31,11 @@ TypeScript implementation of pineapple language (https://github.com/karminski/pi
[karminski/pineapple](https://github.com/karminski/pineapple)

## 说明
pineapple lang 是一个简单的编程语言 demo. 它包含了个手写的递归下降解析器和一个简单的解释器. 虽然该语言甚至不是图灵完备的. 但 pineapple 的主要目的是让编译原理初学者有一个预热, 简单了解一个编程语言是怎么构建的.
pineapple lang 是一个简单的编程语言 demo. 它包含了个手写的递归下降解析器和一个简单的解释器.

该语言现在应该是图灵完备的.

pineapple 的主要目的是让编译原理初学者有一个预热, 简单了解一个编程语言是怎么构建的.

## 运行
```
Expand All @@ -44,4 +48,30 @@ npm run test
- [liulinboyi](https://github.com/liulinboyi)


# 最后把代码转成JavaScript的AST然后使用[javascript的解释器canjs](https://github.com/jrainlau/canjs)执行代码.
# 最后把代码转成[JavaScript的AST](https://astexplorer.net/)然后使用[javascript的解释器canjs](https://github.com/jrainlau/canjs)执行代码.

# 最后算是完成大部分了,实现了递归调用,解决了求斐波那契数问题。

```
# 求斐波那契数
func Fibonacci($a) {
if ($a <= 2) {
return 1
}
$_a = $a - 1
$_b = $a - 2
$aa = Fibonacci($_a)
$bb = Fibonacci($_b)
return $aa + $bb
}
$res = Fibonacci(7)
print($res)
```
21 changes: 21 additions & 0 deletions demo/hello-world-10.pineapple
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 求斐波那契数
func Fibonacci($a) {

if ($a <= 2) {
return 1
}

$_a = $a - 1

$_b = $a - 2

$aa = Fibonacci($_a)

$bb = Fibonacci($_b)

return $aa + $bb
}

$res = Fibonacci(7)

print($res)
17 changes: 17 additions & 0 deletions demo/hello-world-9.pineapple
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# 累加
func add($a) {

if ($a == 1) {
return 1
}

$_a = $a - 1

$aa = add($_a)

return $a + $aa
}

$res = add(3)

print($res)
10 changes: 2 additions & 8 deletions dist/src/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@ function Execute(code) {
// parse
ast = parser_1.parse(code);
for (let i = 0; i < ast.body.length; i++) {
console.log(ast.body[i].type, 'type');
if (ast.body[i].type === "COMMENT") { // 如果是注释,删除
console.log(i);
ast.body.splice(i, 1);
i--;
}
}
console.log(JSON.stringify(ast, null, 4), '\r\rAST');
console.log("--------------------------------------------");
// console.log(JSON.stringify(ast, null, 4), '\r\rAST')
// console.log("--------------------------------------------")
// resolve
const vm = new index_js_1.default(ast);
vm.run();
Expand Down Expand Up @@ -87,19 +85,15 @@ function resolveAssignment(g, assignment) {
function resolvePrint(g, print) {
let varName = "";
varName = print.Variable.Name;
// console.log(varName, 'varName')
// console.log(g, 'g')
if (varName == "") {
throw new Error("resolvePrint(): variable name can NOT be empty.");
}
let str = "";
let ok = false;
str = g.Variables[varName];
// console.log(str, 'str')
ok = str !== null && str !== undefined ? true : false;
if (!ok) {
throw new Error(`resolvePrint(): variable '$${varName}'not found.`);
}
console.log(str);
return null;
}
1 change: 0 additions & 1 deletion dist/src/lexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ class Lexer {
}
// 匹配Token并跳过匹配的Token
MatchToken() {
// console.log(this.sourceCode[0], '当前Token')
// check ignored
if (this.isIgnored()) {
return { lineNum: this.lineNum, tokenType: TOKEN_IGNORED, token: "Ignored" };
Expand Down
7 changes: 5 additions & 2 deletions dist/src/lexer1.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ class Lexer {
// 匹配Token并跳过匹配的Token
MatchToken() {
this.checkCode(this.sourceCode[0]); // 只做检查,不吃字符
// console.log(this.sourceCode[0], '当前Token')
// check ignored
if (this.isIgnored()) {
return { lineNum: this.lineNum, tokenType: exports.TOKEN_IGNORED, token: "Ignored" };
Expand All @@ -168,7 +167,11 @@ class Lexer {
case ')':
this.skipSourceCode(1);
return { lineNum: this.lineNum, tokenType: exports.TOKEN_RIGHT_PAREN, token: ")" };
case '=':
case '=': // =
if (this.sourceCode[1] === "=") { // ==
this.skipSourceCode(2);
return { lineNum: this.lineNum, tokenType: exports.Operator, token: "==" };
}
this.skipSourceCode(1);
return { lineNum: this.lineNum, tokenType: exports.TOKEN_EQUAL, token: "=" };
case '"':
Expand Down
7 changes: 0 additions & 7 deletions dist/src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ function parseStatement(lexer) {
// 向前看一个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 lexer1_1.TOKEN_PRINT:
return Print_1.parsePrint(lexer);
Expand Down Expand Up @@ -81,15 +80,10 @@ function parseNumber(lexer) {
let str = "";
let { tokenType, token } = lexer.LookAhead();
str += token;
// console.log(tokenType, 'parseNumber', str, 'str')
if (tokenType === lexer1_1.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')
// }
lexer.NextTokenIs(lexer1_1.NUMBER);
lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED);
}
Expand Down Expand Up @@ -120,7 +114,6 @@ function parse(code) {
let lexer = lexer1_1.NewLexer(code);
let sourceCode = parseSourceCode(lexer);
lexer.NextTokenIs(lexer1_1.TOKEN_EOF);
// console.log(JSON.stringify(sourceCode), 'sourceCode')
return sourceCode;
}
exports.parse = parse;
10 changes: 0 additions & 10 deletions dist/src/parser/Assignment.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,14 @@ function parseAssignment(lexer) {
lexer.NextTokenIs(lexer1_1.TOKEN_EQUAL); // =
lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); // 空格
const tokenType = lexer.LookAhead().tokenType;
console.log(tokenType, 'lexer.LookAhead().tokenType');
// 如果后面仍是$
if (tokenType === lexer1_1.TOKEN_VAR_PREFIX) {
const Variable = parser_1.parseVariable(lexer); // 标识符,这里面会把邻近的空格回车删掉
console.log(Variable, 'Variable');
const identifier = new Identifier(Variable.Name);
VariableDeclarator.init = identifier;
assignment.type = "VariableDeclaration";
assignment.declarations.push(VariableDeclarator); // 一行只允许声明和初始化一个变量
let ahead = lexer.LookAhead();
console.log(ahead, 'parseAssignment Variable ahead');
if (ahead.tokenType !== lexer1_1.Operator) {
return assignment;
}
Expand All @@ -74,16 +71,13 @@ function parseAssignment(lexer) {
else {
if (tokenType === lexer1_1.TOKEN_NAME) { // 函数执行并赋值
const expression = Expression_1.parseExpression(lexer);
console.log(expression);
VariableDeclarator.init = expression.expression;
assignment.type = "VariableDeclaration";
}
else if (tokenType === lexer1_1.NUMBER) {
// console.log('parseNumber start')
const literial = new Literal(parser_1.parseNumber(lexer)); // 这里面会把邻近的空格回车删掉
VariableDeclarator.init = literial;
assignment.type = "VariableDeclaration";
// console.log('parseNumber end')
}
else {
const literial = new Literal(parser_1.parseString(lexer)); // 这里面会把邻近的空格回车删掉
Expand All @@ -92,7 +86,6 @@ function parseAssignment(lexer) {
}
assignment.declarations.push(VariableDeclarator); // 一行只允许声明和初始化一个变量
let ahead = lexer.LookAhead();
console.log(ahead, 'parseAssignment not Variable ahead');
if (ahead.tokenType !== lexer1_1.Operator) {
return assignment;
}
Expand Down Expand Up @@ -120,21 +113,18 @@ function parseBinaryExpression(lexer, idAndinit, assignment, leftType) {
}
};
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;
}
Expand Down
2 changes: 0 additions & 2 deletions dist/src/parser/Comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ class Comment {
exports.Comment = Comment;
function paseComment(lexer) {
let comment = new Comment();
console.log("paseComment start");
comment.LineNum = lexer.GetLineNum();
lexer.NextTokenIs(lexer1_1.COMMENT);
console.log(lexer.isNewLine(lexer.sourceCode[0]), 'isNewLine');
let content = "";
// 如果换行或者源码为空则停止解析注释
while (!lexer.isNewLine(lexer.sourceCode[0]) && !lexer.isEmpty()) {
Expand Down
2 changes: 1 addition & 1 deletion dist/src/parser/Expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function parseExpression(lexer) {
let p;
// $
if (tokenType === lexer1_1.TOKEN_VAR_PREFIX) {
p = parser_1.parseVariable(lexer);
p = parser_1.parseVariable(lexer).Name;
params.push(new Assignment_1.Identifier(p));
}
else if (tokenType === lexer1_1.NUMBER) {
Expand Down
12 changes: 3 additions & 9 deletions dist/src/parser/Function.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ function paseBlock(lexer, BlockStatementBody) {
}
else if (ahead.tokenType === lexer1_1.TOKEN_VAR_PREFIX) { // $
const VariableDeclaration = Assignment_1.parseAssignment(lexer);
console.log(VariableDeclaration);
BlockStatementBody.push({
type: VariableDeclaration.type,
declarations: VariableDeclaration.declarations,
Expand All @@ -92,16 +91,17 @@ function paseBlock(lexer, BlockStatementBody) {
}
else if (ahead.tokenType === lexer1_1.TOKEN_PRINT) {
const print = Print_1.parsePrint(lexer);
console.log(print);
BlockStatementBody.push(print);
paseBlock(lexer, BlockStatementBody);
}
else if (ahead.tokenType === lexer1_1.TOKEN_IF) {
const IfStatement = IfStatement_1.parseIfStatement(lexer);
console.log(IfStatement);
BlockStatementBody.push(IfStatement);
paseBlock(lexer, BlockStatementBody);
}
// else if (ahead.tokenType === TOKEN_NAME) {
// const ExpressionStatement = parseExpression(lexer)
// }
return BlockStatementBody;
}
exports.paseBlock = paseBlock;
Expand All @@ -112,17 +112,14 @@ function paseReturnStatement(lexer) {
let VariableDeclarator = { type: "VariableDeclarator" };
VariableDeclarator.id = { type: "Identifier" };
const tokenType = lexer.LookAhead().tokenType;
console.log(tokenType, 'lexer.LookAhead().tokenType');
// 如果后面仍是$
if (tokenType === lexer1_1.TOKEN_VAR_PREFIX) {
const Variable = parser_1.parseVariable(lexer); // 标识符,这里面会把邻近的空格回车删掉
console.log(Variable, 'Variable');
const identifier = new Assignment_1.Identifier(Variable.Name);
VariableDeclarator.init = identifier;
assignment.type = "ReturnStatement";
assignment.declarations.push(VariableDeclarator); // 一行只允许声明和初始化一个变量
let ahead = lexer.LookAhead();
console.log(ahead, 'parseAssignment Variable ahead');
if (ahead.tokenType !== lexer1_1.Operator) {
return assignment;
}
Expand All @@ -136,11 +133,9 @@ function paseReturnStatement(lexer) {
}
else {
if (tokenType === lexer1_1.NUMBER) {
// console.log('parseNumber start')
const literial = new Assignment_1.Literal(parser_1.parseNumber(lexer)); // 这里面会把邻近的空格回车删掉
VariableDeclarator.init = literial;
assignment.type = "ReturnStatement";
// console.log('parseNumber end')
}
else {
const literial = new Assignment_1.Literal(parser_1.parseString(lexer)); // 这里面会把邻近的空格回车删掉
Expand All @@ -149,7 +144,6 @@ function paseReturnStatement(lexer) {
}
assignment.declarations.push(VariableDeclarator); // 一行只允许声明和初始化一个变量
let ahead = lexer.LookAhead();
console.log(ahead, 'parseAssignment not Variable ahead');
if (ahead.tokenType !== lexer1_1.Operator) {
return assignment;
}
Expand Down
7 changes: 0 additions & 7 deletions dist/src/parser/IfStatement.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ function parseIfStatement(lexer) {
body: []
};
consequent.body = Function_1.paseBlock(lexer, BlockStatementBody);
console.log(consequent);
lexer.NextTokenIs(lexer1_1.BLOCK_END);
lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); // 去除空格回车等
return {
Expand All @@ -44,32 +43,26 @@ function parseBinaryExpression(lexer) {
}
};
const Variable = parser_1.parseVariable(lexer); // 标识符,这里面会把邻近的空格回车删掉
console.log(Variable, 'Variable');
const identifier = new Assignment_1.Identifier(Variable.Name);
console.log(identifier);
let leftType = identifier.type;
BinaryExpression.operator = (lexer.NextTokenIs(lexer1_1.Operator)).nowToken; // +-*/
lexer.LookAheadAndSkip(lexer1_1.TOKEN_IGNORED); // 空格
let ahead = lexer.LookAhead();
console.log(ahead, 'parseBinaryExpression ahead');
if (leftType === 'Identifier') {
BinaryExpression.left = new Assignment_1.Identifier(identifier.name);
}
else if (leftType === 'Literal') {
// BinaryExpression.left = new Literal((idAndinit.init as Literal).value)
}
if (ahead.tokenType === lexer1_1.NUMBER) {
console.log('NUMBER');
const literial = new Assignment_1.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 Assignment_1.Identifier(Variable.Name);
BinaryExpression.right = identifier;
}
console.log(BinaryExpression);
return BinaryExpression;
}
exports.parseBinaryExpression = parseBinaryExpression;
2 changes: 1 addition & 1 deletion dist/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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-8.pineapple'), { encoding: 'utf-8' });
code = fs.readFileSync(path.resolve(__dirname, '../demo/hello-world-10.pineapple'), { encoding: 'utf-8' });
console.log(code, 'code');
if (code.length > 0) {
Execute(code);
Expand Down
Loading

0 comments on commit 83bfacc

Please sign in to comment.