All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ssssssss.script.parsing.Parser Maven / Gradle / Ivy

The newest version!
package org.ssssssss.script.parsing;

import org.ssssssss.script.MagicScriptError;
import org.ssssssss.script.parsing.ast.*;
import org.ssssssss.script.parsing.ast.binary.AssigmentOperation;
import org.ssssssss.script.parsing.ast.linq.*;
import org.ssssssss.script.parsing.ast.literal.BooleanLiteral;
import org.ssssssss.script.parsing.ast.literal.ByteLiteral;
import org.ssssssss.script.parsing.ast.literal.DoubleLiteral;
import org.ssssssss.script.parsing.ast.literal.FloatLiteral;
import org.ssssssss.script.parsing.ast.literal.IntegerLiteral;
import org.ssssssss.script.parsing.ast.literal.LongLiteral;
import org.ssssssss.script.parsing.ast.literal.NullLiteral;
import org.ssssssss.script.parsing.ast.literal.RegexpLiteral;
import org.ssssssss.script.parsing.ast.literal.ShortLiteral;
import org.ssssssss.script.parsing.ast.literal.StringLiteral;
import org.ssssssss.script.parsing.ast.literal.*;
import org.ssssssss.script.parsing.ast.statement.Spread;
import org.ssssssss.script.parsing.ast.statement.*;

import java.util.*;

import static org.ssssssss.script.parsing.TokenType.*;


/**
 * 语法解析器
 **/
public class Parser {

	public static final String ANONYMOUS_VARIABLE = "-anonymous";
	private static final TokenType[][] BINARY_OPERATOR_PRECEDENCE = new TokenType[][]{
			new TokenType[]{Assignment},
			new TokenType[]{RShift2Equal, RShiftEqual, LShiftEqual, XorEqual, BitOrEqual, BitAndEqual, PercentEqual, ForwardSlashEqual, AsteriskEqual, MinusEqual, PlusEqual},
			new TokenType[]{Or, SqlOr},
			new TokenType[]{And, SqlAnd},
			new TokenType[]{BitOr},
			new TokenType[]{Xor},
			new TokenType[]{BitAnd},
			new TokenType[]{EqualEqualEqual, Equal, NotEqualEqual, NotEqual, SqlNotEqual},
			new TokenType[]{Less, LessEqual, Greater, GreaterEqual, InstanceOf},
			new TokenType[]{Plus, Minus},
			new TokenType[]{LShift, RShift, Rshift2},
			new TokenType[]{Asterisk, ForwardSlash, Percentage}
	};
	private static final TokenType[][] LINQ_BINARY_OPERATOR_PRECEDENCE = new TokenType[][]{
			new TokenType[]{RShift2Equal, RShiftEqual, LShiftEqual, XorEqual, BitOrEqual, BitAndEqual, PercentEqual, ForwardSlashEqual, AsteriskEqual, MinusEqual, PlusEqual},
			new TokenType[]{Or, SqlOr},
			new TokenType[]{And, SqlAnd},
			new TokenType[]{BitOr},
			new TokenType[]{Xor},
			new TokenType[]{BitAnd},
			new TokenType[]{Assignment, EqualEqualEqual, Equal, NotEqualEqual, NotEqual, SqlNotEqual},
			new TokenType[]{Less, LessEqual, Greater, GreaterEqual, InstanceOf},
			new TokenType[]{Plus, Minus},
			new TokenType[]{LShift, RShift, Rshift2},
			new TokenType[]{ForwardSlash, Asterisk, Percentage}
	};
	private static final TokenType[] UNARY_OPERATORS = new TokenType[]{MinusMinus, PlusPlus, BitNot, Minus, Plus, Not};
	private static final List KEYWORDS = Arrays.asList("import", "as", "var", "let", "const", "return", "break", "continue", "if", "for", "in", "new", "true", "false", "null", "else", "try", "catch", "finally", "async", "while", "exit", "and", "or", "throw"/*, "assert"*/);
	private static final List LINQ_KEYWORDS = Arrays.asList("from", "join", "left", "group", "by", "as", "having", "and", "or", "in", "where", "on", "limit", "offset");
	private VarScope varNames = new VarScope();
	private VarScope rootvarNames = varNames;
	private final List spans = new ArrayList<>();
	private final Set varIndices = new LinkedHashSet<>();
	private int varCount = 0;
	private int linqLevel = 0;
	private boolean requiredNew = true;
	private TokenStream stream;
	private final List defines = new ArrayList<>();

	public Set getVarIndices() {
		return varIndices;
	}


	public List parse(String source) {
		List nodes = new ArrayList<>();
		push();
		stream = Tokenizer.tokenize(source);
		while (stream.hasMore()) {
			Node node = parseStatement();
			if (node != null) {
				validateNode(node);
				nodes.add(node);
			}
		}
		pop();
		return nodes;
	}

	private void validateNode(Node node) {
		if (node instanceof Literal) {
			MagicScriptError.error("literal cannot be used alone", node.getSpan());
		}
	}

	private Node parseStatement() {
		return parseStatement(false);
	}

	private Node parseStatement(boolean expectRightCurly) {
		Node result = null;
		if (stream.match("import", false)) {
			result = parseImport();
		} else if (matchVarDefine()) {
			result = parseVarDefine();
		} else if (stream.match("if", false)) {
			result = parseIfStatement();
		} else if (stream.match("return", false)) {
			result = parseReturn();
		} else if (stream.match("for", false)) {
			result = parseForStatement();
		} else if (stream.match("while", false)) {
			result = parseWhileStatement();
		} else if (stream.match("continue", false)) {
			result = new Continue(stream.consume().getSpan());
		} else if (stream.match("async", false)) {
			result = parseAsync();
		} else if (stream.match("try", false)) {
			result = parseTryStatement();
		} else if (stream.match("break", false)) {
			result = new Break(stream.consume().getSpan());
		} else if (stream.match("exit", false)) {
			result = parseExit();
		} else if (stream.match("assert", false)) {
			result = parseAssert();
		} else if (stream.match("throw", false)) {
			result = parseThrow();
		} else {
			int index = stream.makeIndex();
			if (matchTypeDefine()) {
				stream.resetIndex(index);
				result = parseVarDefine();
			}
			if (result == null) {
				stream.resetIndex(index);
				result = parseExpression(expectRightCurly);
			}
		}
		// consume semi-colons as statement delimiters
		while (stream.match(";", true)) {
			;
		}
		return result;
	}

	private boolean matchTypeDefine() {
		boolean typeDefine = stream.match(Identifier, true);
		if (!typeDefine) {
			return false;
		}
		int index = stream.makeIndex();
		try {
			if ("new".equals(stream.getPrev().getText())) {
				return false;
			}
			if (stream.match(Identifier, false)) {
				return true;
			}
			int end = stream.getPrev().getSpan().getEnd();
			if (stream.hasMore() && stream.consume().getSpan().getStart() == end) {
				return false;
			}
		} finally {
			stream.resetIndex(index);
		}
		// destructuring support
		boolean isMapAccess;
		if ((isMapAccess = stream.match(true, LeftCurly)) || stream.match(true, LeftBracket)) {
			do {
				if (!stream.match(true, Identifier)) {
					return false;
				}
			} while (stream.match(true, Comma));
			if (isMapAccess) {
				return stream.match(true, RightCurly);
			} else {
				return stream.match(true, RightBracket);
			}
		}
		return false;
	}

	private boolean matchVarDefine() {
		return stream.match(false, "var", "let", "const");
	}

	private VarIndex add(String name) {
		VarScope varIndices = varNames;
		do {
			for (int j = varIndices.size() - 1; j >= 0; j--) {
				VarIndex varIndex = varIndices.get(j);
				if (varIndex.getName().equals(name)) {
					return defines.contains(name) ? varIndex.scoped() : varIndex;
				}
			}
		} while ((varIndices = varIndices.getParent()) != null);
		return add(new VarIndex(name, varCount++, true), true);
	}

	private VarIndex add(VarIndex varIndex) {
		return add(varIndex, false);
	}

	private VarIndex add(VarIndex varIndex, boolean isRoot) {
		if(defines.contains(varIndex.getName())){
			varIndex = varIndex.scoped();
		}
		if (isRoot) {
			rootvarNames.add(varIndex);
		} else {
			varNames.add(varIndex);
		}
		varIndices.add(varIndex);
		return varIndex;
	}

	private VarIndex forceAdd(String name) {
		return forceAdd(name, false);
	}

	private VarIndex forceAdd(String name, boolean isConst) {
		return add(new VarIndex(name, varCount++, false, isConst));
	}

	private void push() {
		varNames = varNames.push();
	}

	private void pop() {
		varNames = varNames.pop();
	}

	private Span addSpan(Span opening, Span ending) {
		addSpan(opening);
		addSpan(ending);
		return addSpan(new Span(opening, ending));
	}

	private Span addSpan(String source, int start, int end) {
		return addSpan(new Span(source, start, end));
	}

	private Span addSpan(Span span) {
		this.spans.add(span);
		return span;
	}

	private Node parseExit() {
		Span opening = stream.expect("exit").getSpan();
		List expressionList = new ArrayList<>();
		do {
			expressionList.add(parseExpression());
		} while (stream.match(Comma, true));
		return new Exit(addSpan(opening, stream.getPrev().getSpan()), expressionList);
	}

	private Node parseThrow() {
		Span opening = stream.consume().getSpan();
		Expression expression = parseExpression();
		return new Throw(addSpan(opening, stream.getPrev().getSpan()), expression);
	}

	private Node parseAssert() {
		int index = stream.makeIndex();
		try {
			Span opening = stream.expect("assert").getSpan();
			Expression condition = parseExpression();
			stream.expect(Colon);
			List expressionList = new ArrayList<>();
			do {
				expressionList.add(parseExpression());
			} while (stream.match(Comma, true));
			return new Assert(addSpan(opening, stream.getPrev().getSpan()), condition, expressionList);
		} catch (Exception e) {
			stream.resetIndex(index);
			return parseExpression();
		}
	}

	private Expression parseAsync() {
		Span opening = stream.expect("async").getSpan();
		requiredNew = false;
		Expression expression = parseExpression();
		requiredNew = true;
		if (expression instanceof MethodCall || expression instanceof FunctionCall || expression instanceof LambdaFunction) {
			return new AsyncCall(addSpan(opening, stream.getPrev().getSpan()), expression);
		}
		MagicScriptError.error("Expected MethodCall or FunctionCall or LambdaFunction", stream.getPrev().getSpan());
		return null;
	}

	private Import parseImport() {
		Span opening = stream.expect("import").getSpan();
		if (stream.hasMore()) {
			Token expected = stream.consume();
			String packageName = null;
			boolean isStringLiteral = expected.getType() == StringLiteral;
			if (isStringLiteral) {
				packageName = createStringLiteral(expected).getValue();
			} else if (expected.getType() == Identifier) {
				Span startSpan = expected.getSpan();
				packageName = startSpan.getText();
				while (stream.match(true, Period)){
					isStringLiteral = true;
					if(stream.match(false, Asterisk)){
						expected = stream.consume();
						break;
					}
					expected = stream.expect(Identifier);
				}
				if(isStringLiteral){
					packageName = new Span(startSpan, expected.getSpan()).getText();
				}
			} else {
				MagicScriptError.error("Expected identifier or string, but got stream is " + expected.getType().getError(), stream.getPrev().getSpan());
			}
			String varName = packageName;
			if (isStringLiteral) {
				if (stream.match("as", true)) {
					expected = stream.expect(Identifier);
					checkKeyword(expected.getSpan());
					varName = expected.getSpan().getText();
				} else {
					String temp = packageName;
					if (!temp.startsWith("@")) {
						int index = temp.lastIndexOf(".");
						if (index != -1) {
							temp = temp.substring(index + 1);
						}
					} else {
						MagicScriptError.error("Expected as", stream);
					}
					varName = temp;
				}
			}
			return new Import(addSpan(opening, expected.getSpan()), packageName, forceAdd(varName), !isStringLiteral);
		}
		MagicScriptError.error("Expected identifier or string, but got stream is EOF", stream.getPrev().getSpan());
		return null;
	}

	private TryStatement parseTryStatement() {
		Token opening = stream.expect("try");
		push();
		List tryResources = new ArrayList<>();
 		if (stream.match("(", true)) {
			if (stream.match(")", false)) {
				// 空的 try-with-resource
			} else {
				while (!stream.match(")", false)) {
					if (stream.match(";", true)) {
						continue;
					}
					VariableDefine result = null;
					if (matchVarDefine()) {
						result = parseVarDefine();
					} else {
						if (stream.match(false, KEYWORDS.toArray(new String[0]))) {
							MagicScriptError.error("try 括号中只允许写赋值语句", stream.consume().getSpan());
						}
						int index = stream.makeIndex();
						if (matchTypeDefine()) {
							stream.resetIndex(index);
							result = parseVarDefine();
						}
						if (result == null) {
							stream.resetIndex(index);
							MagicScriptError.error("try 括号中只允许写赋值语句", stream.consume().getSpan());
						}
					}
					tryResources.add(result);
				}
			}
			stream.expect(")");
		}
		List tryBlocks = parseFunctionBody();
		pop();
		List catchBlocks = new ArrayList<>();
		List finallyBlocks = new ArrayList<>();
		VarIndex exceptionVarNode = null;
		if (stream.match("catch", true)) {
			push();
			if (stream.match("(", true)) {
				exceptionVarNode = add(stream.expect(Identifier).getText());
				defines.add(exceptionVarNode.getName());
				stream.expect(")");
			}
			catchBlocks.addAll(parseFunctionBody());
			pop();
		}
		if (stream.match("finally", true)) {
			push();
			finallyBlocks.addAll(parseFunctionBody());
			pop();
		}
		return new TryStatement(addSpan(opening.getSpan(), stream.getPrev().getSpan()), exceptionVarNode, tryBlocks, tryResources, catchBlocks, finallyBlocks);
	}

	private List parseFunctionBody() {
		stream.expect("{");
		List blocks = new ArrayList<>();
		while (stream.hasMore() && !stream.match("}", false)) {
			Node node = parseStatement(true);
			if (node != null) {
				validateNode(node);
				blocks.add(node);
			}
		}
		expectCloseing();
		return blocks;
	}

	private Expression parseNewExpression(Span opening) {
		Expression expression = parseAccessOrCall(Identifier, true);
		if (expression instanceof MethodCall) {
			MethodCall call = (MethodCall) expression;
			Span span = addSpan(opening.getSource(), opening.getStart(), stream.getPrev().getSpan().getEnd());
			return parseAccessOrCall(new NewStatement(span, call.getMethod(), call.getArguments()));
		} else if (expression instanceof FunctionCall) {
			FunctionCall call = (FunctionCall) expression;
			Span span = addSpan(opening.getSource(), opening.getStart(), stream.getPrev().getSpan().getEnd());
			return parseAccessOrCall(new NewStatement(span, call.getFunction(), call.getArguments()));
		}
		MagicScriptError.error("Expected MethodCall or FunctionCall or LambdaFunction", stream.getPrev().getSpan());
		return null;
	}

	private VariableDefine parseVarDefine() {
		Span opening = stream.consume().getSpan();
		boolean isMapAccess;
		if ((isMapAccess = stream.match(false, LeftCurly)) || stream.match(false, LeftBracket)) {
			stream.expect(LeftCurly, LeftBracket);
			List tokens = new ArrayList<>();
			do {
				Token token = stream.expect(Identifier);
				tokens.add(token);
			} while (stream.match(true, Comma));
			if (isMapAccess) {
				stream.expect(RightCurly);
			} else {
				stream.expect(RightBracket);
			}
			stream.match(Assignment, true);
			boolean isConst = "const".equals(opening.getText());
			VariableDestructuringDefine destructuring = new VariableDestructuringDefine(addSpan(opening, stream.getPrev().getSpan()), tokens.size(), parseExpression(), isMapAccess);
			for (Token token : tokens) {
				String variableName = token.getSpan().getText();
				VarIndex varIndex = forceAdd(variableName, isConst);
				defines.add(variableName);
				VariableDefine variableDefine = new VariableDefine(addSpan(token.getSpan(), stream.getPrev().getSpan()), varIndex, null);
				destructuring.add(variableDefine);
			}
			return destructuring;
		}
		Token token = stream.expect(Identifier);
		boolean isConst = "const".equals(opening.getText());
		checkKeyword(token.getSpan());
		String variableName = token.getSpan().getText();
		if (stream.match(Assignment, true)) {
			VarIndex varIndex = forceAdd(variableName, isConst);
			defines.add(variableName);
			return new VariableDefine(addSpan(opening, stream.getPrev().getSpan()), varIndex, parseExpression());
		} else if (isConst) {
			MagicScriptError.error("const修饰的变量需要给初始值", stream.getPrev().getSpan());
		}
		return new VariableDefine(addSpan(opening, stream.getPrev().getSpan()), forceAdd(variableName), null);
	}

	private void checkKeyword(Span span) {
		if (KEYWORDS.contains(span.getText())) {
			MagicScriptError.error("变量名不能定义为关键字", span);
		}
	}

	private WhileStatement parseWhileStatement() {
		Span openingWhile = stream.expect("while").getSpan();
		requiredNew = false;
		Expression condition = parseExpression();
		requiredNew = true;
		push();
		List trueBlock = parseFunctionBody();
		Span closingEnd = stream.getPrev().getSpan();
		pop();
		return new WhileStatement(addSpan(openingWhile, closingEnd), condition, trueBlock);
	}

	private ForStatement parseForStatement() {
		Span openingFor = stream.expect("for").getSpan();
		stream.expect("(");
		push();
		Span index = null;
		Span value = stream.expect(Identifier).getSpan();
		checkKeyword(value);
		defines.add(value.getText());
		if (stream.match(Comma, true)) {
			index = value;
			value = stream.expect(Identifier).getSpan();
			defines.add(value.getText());
			checkKeyword(value);
		}
		VarIndex indexOrKeyNode = null;
		if (index != null) {
			indexOrKeyNode = forceAdd(index.getText());
		}
		VarIndex valueNode = forceAdd(value.getText());
		stream.expect("in");
		Expression mapOrArray = parseExpression();
		stream.expect(")");
		List body = parseFunctionBody();
		VarIndex anonymous = forceAdd(ANONYMOUS_VARIABLE);
		pop();
		return new ForStatement(addSpan(openingFor, stream.getPrev().getSpan()), indexOrKeyNode, valueNode, anonymous, mapOrArray, body);
	}

	private Span expectCloseing() {
		if (!stream.hasMore()) {
			MagicScriptError.error("Did not find closing }.", stream.prev().getSpan());
		}
		return stream.expect("}").getSpan();
	}

	private Node parseIfStatement() {
		Span openingIf = stream.expect("if").getSpan();
		requiredNew = false;
		Expression condition = parseExpression();
		requiredNew = true;
		push();
		List trueBlock = parseFunctionBody();
		pop();
		List elseIfs = new ArrayList<>();
		List falseBlock = new ArrayList<>();
		while (stream.hasMore() && stream.match("else", true)) {
			if (stream.hasMore() && stream.match("if", false)) {
				Span elseIfOpening = stream.expect("if").getSpan();
				Expression elseIfCondition = parseExpression();
				push();
				List elseIfBlock = parseFunctionBody();
				Span elseIfSpan = addSpan(elseIfOpening, elseIfBlock.size() > 0 ? elseIfBlock.get(elseIfBlock.size() - 1).getSpan() : elseIfOpening);
				pop();
				elseIfs.add(new IfStatement(elseIfSpan, elseIfCondition, elseIfBlock, new ArrayList<>(), new ArrayList<>()));
			} else {
				push();
				falseBlock.addAll(parseFunctionBody());
				pop();
				break;
			}
		}
		Span closingEnd = stream.getPrev().getSpan();

		return new IfStatement(addSpan(openingIf, closingEnd), condition, trueBlock, elseIfs, falseBlock);
	}

	private Node parseReturn() {
		Span returnSpan = stream.expect("return").getSpan();
		if (stream.match(false, ";", "}")) {
			return new Return(returnSpan, null);
		}
		Expression returnValue = parseExpression();
		return new Return(addSpan(returnSpan, returnValue.getSpan()), returnValue);
	}

	public Expression parseExpression() {
		return parseTernaryOperator();
	}

	public Expression parseExpression(boolean expectRightCurly) {
		return parseTernaryOperator(expectRightCurly);
	}

	private Expression parseTernaryOperator(boolean expectRightCurly) {
		Expression condition = parseBinaryOperator(0, expectRightCurly);
		if (stream.match(QuestionMark, true)) {
			Expression trueExpression = parseTernaryOperator(expectRightCurly);
			stream.expect(Colon);
			Expression falseExpression = parseTernaryOperator(expectRightCurly);
			if (condition instanceof AssigmentOperation) {
				AssigmentOperation operation = (AssigmentOperation) condition;
				operation.setRightOperand(new TernaryOperation(operation.getRightOperand(), trueExpression, falseExpression));
				return operation;
			}
			return new TernaryOperation(condition, trueExpression, falseExpression);
		} else {
			return condition;
		}
	}

	private Expression parseTernaryOperator() {
		return parseTernaryOperator(false);
	}

	private Expression parseBinaryOperator(TokenType[][] precedence, int level, boolean expectRightCurly) {
		int nextLevel = level + 1;
		Expression left = nextLevel == precedence.length ? parseUnaryOperator(expectRightCurly) : parseBinaryOperator(nextLevel, expectRightCurly);

		TokenType[] operators = precedence[level];
		while (stream.hasMore() && stream.match(false, operators)) {
			Token operator = stream.consume();
			if (operator.getType().isInLinq() && linqLevel == 0) {
				MagicScriptError.error(operator.getText() + " 只能在Linq中使用", stream);
			}
			Expression right = nextLevel == precedence.length ? parseUnaryOperator(expectRightCurly) : parseBinaryOperator(nextLevel, expectRightCurly);
			left = BinaryOperation.create(left, operator, right, linqLevel);
		}
		addSpan(left.getSpan());
		return left;
	}

	private Expression parseBinaryOperator(int level, boolean expectRightCurly) {
		if (linqLevel > 0) {
			return parseBinaryOperator(LINQ_BINARY_OPERATOR_PRECEDENCE, level, expectRightCurly);
		}
		return parseBinaryOperator(BINARY_OPERATOR_PRECEDENCE, level, expectRightCurly);
	}


	private Expression parseUnaryOperator(boolean expectRightCurly) {
		if (stream.match(false, UNARY_OPERATORS)) {
			Token operator = stream.consume();
			Expression operand = parseUnaryOperator(expectRightCurly);
			if (operator.getType() == TokenType.Minus && operand instanceof NumberLiteral) {
				((NumberLiteral) operand).setNeg(true);
				return operand;
			}
			return new UnaryOperation(operator, operand);
		} else {
			if (stream.match(LeftParantheses, false)) {    //(
				Span openSpan = stream.expect(LeftParantheses).getSpan();
				int index = stream.makeIndex();
				List parameters = new ArrayList<>();
				push();
				try {
					while (stream.match(Identifier, false)) {
						Token identifier = stream.expect(Identifier);
						checkKeyword(identifier.getSpan());
						if (requiredNew) {
							parameters.add(forceAdd(identifier.getSpan().getText()));
						} else {
							parameters.add(add(identifier.getSpan().getText()));
						}
						if (stream.match(Comma, true)) { //,
							continue;
						}
						if (stream.match(RightParantheses, true)) {  //)
							if (stream.match(Lambda, true)) {   // =>
								return parseLambdaBody(openSpan, parameters);
							}
							break;
						}
					}
					if (stream.match(RightParantheses, true) && stream.match(Lambda, true)) {
						return parseLambdaBody(openSpan, parameters);
					}
				} finally {
					pop();
				}
				stream.resetIndex(index);
				Expression expression = parseExpression();
				stream.expect(RightParantheses);
				return parseAccessOrCall(expression);
			} else {
				Expression expression = parseAccessOrCallOrLiteral(expectRightCurly);
				if (expression instanceof VariableSetter) {
					if (stream.match(false, PlusPlus, MinusMinus)) {
						return new UnaryOperation(stream.consume(), expression, true);
					}
				}

				return expression;
			}
		}
	}

	private Expression parseLambdaBody(Span openSpan, List parameters) {
		defines.clear();
		int index = stream.makeIndex();
		List childNodes = new ArrayList<>();
		try {
			Expression expression = parseExpression();
			childNodes.add(new Return(new Span("return", 0, 6), expression));
			return new LambdaFunction(addSpan(openSpan, expression.getSpan()), parameters, childNodes);
		} catch (Exception e) {
			stream.resetIndex(index);
			if (stream.match(LeftCurly, true)) {
				while (stream.hasMore() && !stream.match(false, "}")) {
					Node node = parseStatement(true);
					validateNode(node);
					childNodes.add(node);
				}
				Span closeSpan = expectCloseing();
				return new LambdaFunction(addSpan(openSpan, closeSpan), parameters, childNodes);
			} else {
				Node node = parseStatement();
				childNodes.add(new Return(addSpan("return", 0, 6), node));
				return new LambdaFunction(addSpan(openSpan, node.getSpan()), parameters, childNodes);
			}
		} finally {
			defines.clear();
		}
	}

	private Expression parseSpreadAccess(Token spread) {
		Expression target = parseExpression();
		return new Spread(addSpan(spread.getSpan(), target.getSpan()), target);
	}

	private Expression parseSpreadAccess() {
		Token spread = stream.expect(Spread);
		return parseSpreadAccess(spread);
	}

	private Expression parseSelect() {
		Span opeing = stream.expect("select", true).getSpan();
		linqLevel++;
		List fields = parseLinqFields();
		stream.expect("from", true);
		LinqField from = parseLinqField();
		List joins = parseLinqJoins();
		LinqExpression where = null;
		if (stream.match("where", true, true)) {
			where = new LinqExpression(parseExpression());
		}
		List groups = parseGroup();
		LinqExpression having = null;
		if (stream.match("having", true, true)) {
			having = new LinqExpression(parseExpression());
		}
		List orders = parseLinqOrders();
		linqLevel--;
		Expression limit = null;
		Expression offset = null;
		if (stream.match("limit", true, true)) {
			limit = parseExpression();
			if (stream.match("offset", true, true)) {
				offset = parseExpression();

			}
		}
		Span close = stream.getPrev().getSpan();
		return new LinqSelect(addSpan(opeing, close), fields, from, joins, where, groups, having, orders, limit, offset);
	}

	private List parseGroup() {
		List groups = new ArrayList<>();
		if (stream.match("group", true, true)) {
			stream.expect("by", true);
			do {
				Expression expression = parseExpression();
				groups.add(new LinqField(expression.getSpan(), expression, null));
			} while (stream.match(Comma, true));
		}
		return groups;
	}

	private List parseLinqOrders() {
		List orders = new ArrayList<>();
		if (stream.match("order", true, true)) {
			stream.expect("by", true);
			do {
				Expression expression = parseExpression();
				int order = 1;
				if (stream.match(false, true, "desc", "asc")) {
					if ("desc".equalsIgnoreCase(stream.consume().getText())) {
						order = -1;
					}
				}
				orders.add(new LinqOrder(addSpan(expression.getSpan(), stream.getPrev().getSpan()), expression, null, order));
			} while (stream.match(Comma, true));
		}
		return orders;
	}

	private List parseLinqFields() {
		List fields = new ArrayList<>();
		do {
			Expression expression = parseExpression();

			if (stream.match(Identifier, false) && !stream.match(LINQ_KEYWORDS, false, true)) {
				if (expression instanceof WholeLiteral) {
					MagicScriptError.error("* 后边不能跟别名", stream);
				} else if (expression instanceof MemberAccess && ((MemberAccess) expression).isWhole()) {
					MagicScriptError.error(expression.getSpan().getText() + " 后边不能跟别名", stream);
				}
				Span alias = stream.consume().getSpan();
				fields.add(new LinqField(addSpan(expression.getSpan(), alias), expression, add(alias.getText())));
			} else {
				fields.add(new LinqField(expression.getSpan(), expression, null));
			}
		} while (stream.match(Comma, true));    //,
		if (fields.isEmpty()) {
			MagicScriptError.error("至少要查询一个字段", stream);
		}
		return fields;
	}

	private List parseLinqJoins() {
		List joins = new ArrayList<>();
		do {
			boolean isLeft = stream.match("left", false, true);
			Span opeing = isLeft ? stream.consume().getSpan() : null;
			if (stream.match("join", true, true)) {
				opeing = isLeft ? opeing : stream.getPrev().getSpan();
				LinqField target = parseLinqField();
				stream.expect("on", true);
				Expression condition = parseExpression();
				joins.add(new LinqJoin(addSpan(opeing, stream.getPrev().getSpan()), isLeft, target, condition));
			}
		} while (stream.match(false, true, "left", "join"));
		return joins;
	}

	private LinqField parseLinqField() {
		Expression expression = parseExpression();
		if (stream.match(Identifier, false) && !stream.match(LINQ_KEYWORDS, false, true)) {
			Span alias = stream.expect(Identifier).getSpan();
			return new LinqField(addSpan(expression.getSpan(), alias), expression, add(alias.getText()));
		}
		return new LinqField(expression.getSpan(), expression, null);
	}

	private Expression parseAccessOrCallOrLiteral(boolean expectRightCurly) {
		Expression expression = null;
		if (expectRightCurly && stream.match("}", false)) {
			return null;
		} else if (stream.match(Spread, false)) {
			expression = parseSpreadAccess();
		} else if (stream.match(Identifier, false)) {
			if (stream.match("async", false)) {
				expression = parseAsync();
			} else if (stream.match("select", false, true)) {
				expression = parseSelect();
			} else {
				expression = parseAccessOrCall(TokenType.Identifier, false);
			}
		} else if (stream.match(LeftCurly, false)) {
			expression = parseMapLiteral();
		} else if (stream.match(LeftBracket, false)) {
			expression = parseListLiteral();
		} else if (stream.match(StringLiteral, false)) {
			expression = createStringLiteral(stream.expect(StringLiteral));
		} else if (stream.match(BooleanLiteral, false)) {
			expression = new BooleanLiteral(stream.expect(BooleanLiteral).getSpan());
		} else if (stream.match(DoubleLiteral, false)) {
			expression = new DoubleLiteral(stream.expect(DoubleLiteral).getSpan());
		} else if (stream.match(FloatLiteral, false)) {
			expression = new FloatLiteral(stream.expect(FloatLiteral).getSpan());
		} else if (stream.match(ByteLiteral, false)) {
			Token token = stream.expect(ByteLiteral);
			expression = token.getValue() != null ? new ByteLiteral(token.getSpan(), token.getValue()) : new ByteLiteral(token.getSpan());
		} else if (stream.match(ShortLiteral, false)) {
			expression = new ShortLiteral(stream.expect(ShortLiteral).getSpan());
		} else if (stream.match(IntegerLiteral, false)) {
			Token token = stream.expect(IntegerLiteral);
			expression = token.getValue() != null ? new IntegerLiteral(token.getSpan(), token.getValue()) : new IntegerLiteral(token.getSpan());
		} else if (stream.match(LongLiteral, false)) {
			Token token = stream.expect(LongLiteral);
			expression = token.getValue() != null ? new LongLiteral(token.getSpan(), token.getValue()) : new LongLiteral(token.getSpan());
		} else if (stream.match(DecimalLiteral, false)) {
			expression = new BigDecimalLiteral(stream.expect(DecimalLiteral).getSpan());
		} else if (stream.match(RegexpLiteral, false)) {
			Token token = stream.expect(RegexpLiteral);
			expression = new RegexpLiteral(token.getSpan(), token);
		} else if (stream.match(NullLiteral, false)) {
			expression = new NullLiteral(stream.expect(NullLiteral).getSpan());
		} else if (linqLevel > 0 && stream.match(Asterisk, false)) {
			expression = new WholeLiteral(stream.expect(Asterisk).getSpan());
		} else if (stream.match(Language, false)) {
			expression = new LanguageExpression(stream.consume().getSpan(), stream.consume().getSpan());
		}
		if (expression == null) {
			MagicScriptError.error("Expected a variable, field, map, array, function or method call, or literal.", stream);
		}
		return parseAccessOrCall(expression);
	}

	private StringLiteral createStringLiteral(Token token) {
		if (token.getTokenStream() == null) {
			return new StringLiteral(token);
		}
		TokenStream tempStream = this.stream;
		this.stream = token.getTokenStream();
		List expressionList = new ArrayList<>();
		while (this.stream.hasMore()) {
			expressionList.add(parseExpression());
		}
		this.stream = tempStream;
		return new StringLiteral(token, expressionList);
	}


	private Expression parseMapLiteral() {
		Span openCurly = stream.expect(LeftCurly).getSpan();

		List keys = new ArrayList<>();
		List values = new ArrayList<>();
		while (stream.hasMore() && !stream.match("}", false)) {
			Expression key;
			if (stream.hasPrev()) {
				Token prev = stream.getPrev();
				if (stream.match(Spread, false) && (prev.getType() == LeftCurly || prev.getType() == Comma)) {
					Token spread = stream.expect(Spread);
					keys.add(null);
					values.add(parseSpreadAccess(spread));
					if (stream.match(false, Comma, RightCurly)) {
						stream.match(Comma, true);
					}
					continue;
				}
			}
			boolean isStringKey;
			if (isStringKey = stream.match(StringLiteral, false)) {	// "key" 'key' """key"""
				key = createStringLiteral(stream.expect(StringLiteral));
			} else if (stream.match(LeftBracket, true)) {	// [key]
				key = parseExpression();
				stream.expect(RightBracket);
			} else {	// key
				key = createStringLiteral(stream.expect(Identifier));
			}
			keys.add(key);
			if (stream.match(false, Comma, RightCurly)) {
				stream.match(Comma, true);
				if (!isStringKey && !(key instanceof VariableAccess)) {
					values.add(new VariableAccess(key.getSpan(), add(key.getSpan().getText())));
				} else {
					values.add(key);
				}
			} else {
				stream.expect(":");
				values.add(parseExpression());
				if (!stream.match("}", false)) {
					stream.expect(Comma);
				}
			}
		}
		Span closeCurly = stream.expect("}").getSpan();
		return new MapLiteral(addSpan(openCurly, closeCurly), keys, values);
	}

	private Expression parseListLiteral() {
		Span openBracket = stream.expect(LeftBracket).getSpan();

		List values = new ArrayList<>();
		while (stream.hasMore() && !stream.match(RightBracket, false)) {
			values.add(parseExpression());
			if (!stream.match(RightBracket, false)) {
				stream.expect(Comma);
			}
		}

		Span closeBracket = stream.expect(RightBracket).getSpan();
		return new ListLiteral(addSpan(openBracket, closeBracket), values);
	}


	private Expression parseAccessOrCall(TokenType tokenType, boolean isNew) {
		Token token = stream.expect(tokenType);
		Span identifier = token.getSpan();
		if (tokenType == Identifier && "new".equals(identifier.getText())) {
			return parseNewExpression(identifier);
		}
		if (tokenType == Identifier && stream.match(Lambda, true)) {
			push();
			String name = identifier.getText();
			Expression expression = parseLambdaBody(identifier, Collections.singletonList(requiredNew ? forceAdd(name) : add(name)));
			pop();
			return expression;
		}
		Expression result = tokenType == StringLiteral ? createStringLiteral(token) : new VariableAccess(identifier, add(identifier.getText()));
		return parseAccessOrCall(result, isNew);
	}

	private Expression parseAccessOrCall(Expression target) {
		return parseAccessOrCall(target, false);
	}

	private Expression parseAccessOrCall(Expression target, boolean isNew) {
		while (stream.hasMore() && stream.match(false, LeftParantheses, LeftBracket, Period, QuestionPeriod, ColonColon)) {
			if (stream.match(ColonColon, false)) {
				Span open = stream.consume().getSpan();
				List arguments = Collections.emptyList();
				Token identifier = stream.expect(Identifier);
				Span closing = identifier.getSpan();
				if (stream.match(LeftParantheses, false)) {
					arguments = parseArguments();
					closing = stream.expect(RightParantheses).getSpan();
				}
				target = new ClassConverter(addSpan(open, closing), identifier.getText(), target, arguments);
			}
			// function or method call
			else if (stream.match(LeftParantheses, false)) {
				List arguments = parseArguments();
				Span closingSpan = stream.expect(RightParantheses).getSpan();
				if (target instanceof VariableAccess || target instanceof MapOrArrayAccess) {
					target = new FunctionCall(addSpan(target.getSpan(), closingSpan), target, arguments, linqLevel > 0);
				}
				else if (target instanceof MemberAccess) {
					target = new MethodCall(addSpan(target.getSpan(), closingSpan), (MemberAccess) target, arguments, linqLevel > 0);
				} else {
					MagicScriptError.error("Expected a variable, field or method.", stream);
				}
				if (isNew) {
					break;
				}
			}

			// map or array access
			else if (stream.match(LeftBracket, true)) {
				Expression keyOrIndex = parseExpression();
				Span closingSpan = stream.expect(RightBracket).getSpan();
				target = new MapOrArrayAccess(addSpan(target.getSpan(), closingSpan), target, keyOrIndex);
			}

			// field or method access
			else if (stream.match(false, Period, QuestionPeriod)) {
				boolean optional = stream.consume().getType() == QuestionPeriod;
				if (linqLevel > 0 && stream.match(Asterisk, false)) {
					target = new MemberAccess(target, optional, stream.expect(Asterisk).getSpan(), true);
				} else {
					target = new MemberAccess(target, optional, stream.expect(Identifier, SqlAnd, SqlOr).getSpan(), false);
				}
			}
		}
		return target;
	}

	/**
	 * Does not consume the closing parentheses.
	 **/
	private List parseArguments() {
		stream.expect(LeftParantheses);
		List arguments = new ArrayList();
		while (stream.hasMore() && !stream.match(RightParantheses, false)) {
			arguments.add(parseExpression());
			if (!stream.match(RightParantheses, false)) {
				stream.expect(Comma);
			}
		}
		return arguments;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy