mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 08:44:20 +03:00
Добавлен оператор match (Pattern Matching)
This commit is contained in:
parent
794d6f2d76
commit
4cb076ca6d
@ -27,37 +27,18 @@ def show_github_events(event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def github_event_type(event) {
|
def github_event_type(event) {
|
||||||
type = event.type
|
|
||||||
repo = "https://github.com/" + event.repo.name
|
repo = "https://github.com/" + event.repo.name
|
||||||
payload = event.payload
|
payload = event.payload
|
||||||
|
return match event.type {
|
||||||
if (type == "CommitCommentEvent") {
|
case "CommitCommentEvent": "commented commit in " + repo + "\n" + payload.comment.body
|
||||||
return "commented commit in " + repo + "\n" + payload.comment.body
|
case "CreateEvent": "created " + payload.ref_type + " on " + repo
|
||||||
|
case "DeleteEvent": "deleted " + payload.ref_type + " on " + repo
|
||||||
|
case "ForkEvent": "forked repository " + repo
|
||||||
|
case "IssueCommentEvent": "commented issue " + payload.issue.title + " on " + repo + "\n" + payload.comment.body
|
||||||
|
case "IssuesEvent": payload.action + " issue '" + payload.issue.title + "' on " + repo
|
||||||
|
case "PullRequestEvent": payload.action + " pull request #" + payload.number + " '" + payload.pull_request.title + "' on " + repo
|
||||||
|
case "PushEvent": "pushed " + length(payload.commits) + " commits to " + repo
|
||||||
|
case "WatchEvent": "start watching repository " + repo
|
||||||
|
case type : type + " on " + repo
|
||||||
}
|
}
|
||||||
if (type == "CreateEvent") {
|
|
||||||
return "created " + payload.ref_type + " on " + repo
|
|
||||||
}
|
|
||||||
if (type == "DeleteEvent") {
|
|
||||||
return "deleted " + payload.ref_type + " on " + repo
|
|
||||||
}
|
|
||||||
if (type == "ForkEvent") {
|
|
||||||
return "forked repository " + repo
|
|
||||||
}
|
|
||||||
if (type == "IssueCommentEvent") {
|
|
||||||
return "commented issue " + payload.issue.title + " on " + repo + "\n" + payload.comment.body
|
|
||||||
}
|
|
||||||
if (type == "IssuesEvent") {
|
|
||||||
return payload.action + " issue '" + payload.issue.title + "' on " + repo
|
|
||||||
}
|
|
||||||
if (type == "PullRequestEvent") {
|
|
||||||
pr = payload.pull_request
|
|
||||||
return payload.action + " pull request #" + payload.number + " '" + pr.title + "' on " + repo
|
|
||||||
}
|
|
||||||
if (type == "PushEvent") {
|
|
||||||
return "pushed " + length(payload.commits) + " commits to " + repo
|
|
||||||
}
|
|
||||||
if (type == "WatchEvent") {
|
|
||||||
return "start watching repository " + repo
|
|
||||||
}
|
|
||||||
return type + " on " + repo
|
|
||||||
}
|
}
|
||||||
|
17
program.own
17
program.own
@ -81,7 +81,7 @@ for i = 0, i < 4, i = i + 1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// map
|
// map
|
||||||
map = {"+" : add, "-" : sub. "*" : mul, "/" : div}
|
map = {"+" : add, "-" : sub, "*" : mul, "/" : div}
|
||||||
map["%"] = def(x,y) = x % y
|
map["%"] = def(x,y) = x % y
|
||||||
map["pow"] = def(x,y) = pow(x, y)
|
map["pow"] = def(x,y) = pow(x, y)
|
||||||
println map["+"]
|
println map["+"]
|
||||||
@ -150,3 +150,18 @@ println jsonencode({
|
|||||||
10: "1000"
|
10: "1000"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
println ""
|
||||||
|
|
||||||
|
|
||||||
|
def fact1(n) = match n {
|
||||||
|
case 0: 1
|
||||||
|
case n: n * fact1(n - 1)
|
||||||
|
}
|
||||||
|
def fact2(n) = match n {
|
||||||
|
case n if n == 0: 1
|
||||||
|
case _: n * fact2(n - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
println fact1(6)
|
||||||
|
println fact2(6)
|
@ -40,4 +40,8 @@ public final class Variables {
|
|||||||
public static void set(String key, Value value) {
|
public static void set(String key, Value value) {
|
||||||
variables.put(key, value);
|
variables.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void remove(String key) {
|
||||||
|
variables.remove(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,8 @@ public final class Lexer {
|
|||||||
case "def": addToken(TokenType.DEF); break;
|
case "def": addToken(TokenType.DEF); break;
|
||||||
case "return": addToken(TokenType.RETURN); break;
|
case "return": addToken(TokenType.RETURN); break;
|
||||||
case "use": addToken(TokenType.USE); break;
|
case "use": addToken(TokenType.USE); break;
|
||||||
|
case "match": addToken(TokenType.MATCH); break;
|
||||||
|
case "case": addToken(TokenType.CASE); break;
|
||||||
default:
|
default:
|
||||||
addToken(TokenType.WORD, word);
|
addToken(TokenType.WORD, word);
|
||||||
break;
|
break;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package com.annimon.ownlang.parser;
|
package com.annimon.ownlang.parser;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.NumberValue;
|
||||||
|
import com.annimon.ownlang.lib.StringValue;
|
||||||
import com.annimon.ownlang.lib.UserDefinedFunction;
|
import com.annimon.ownlang.lib.UserDefinedFunction;
|
||||||
import com.annimon.ownlang.parser.ast.*;
|
import com.annimon.ownlang.parser.ast.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -250,6 +252,53 @@ public final class Parser {
|
|||||||
return new ArrayAccessExpression(variable, indices);
|
return new ArrayAccessExpression(variable, indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MatchExpression match() {
|
||||||
|
// match expression {
|
||||||
|
// case pattern1: result1
|
||||||
|
// case pattern2 if extr: result2
|
||||||
|
// }
|
||||||
|
final Expression expression = expression();
|
||||||
|
consume(TokenType.LBRACE);
|
||||||
|
final List<MatchExpression.Pattern> patterns = new ArrayList<>();
|
||||||
|
do {
|
||||||
|
consume(TokenType.CASE);
|
||||||
|
MatchExpression.Pattern pattern = null;
|
||||||
|
final Token current = get(0);
|
||||||
|
if (match(TokenType.NUMBER)) {
|
||||||
|
pattern = new MatchExpression.ConstantPattern(
|
||||||
|
new NumberValue(Double.parseDouble(current.getText()))
|
||||||
|
);
|
||||||
|
} else if (match(TokenType.HEX_NUMBER)) {
|
||||||
|
pattern = new MatchExpression.ConstantPattern(
|
||||||
|
new NumberValue(Long.parseLong(current.getText(), 16))
|
||||||
|
);
|
||||||
|
} else if (match(TokenType.TEXT)) {
|
||||||
|
pattern = new MatchExpression.ConstantPattern(
|
||||||
|
new StringValue(current.getText())
|
||||||
|
);
|
||||||
|
} else if (match(TokenType.WORD)) {
|
||||||
|
pattern = new MatchExpression.VariablePattern(current.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pattern == null) {
|
||||||
|
throw new ParseException("Wrong pattern in match expression: " + current);
|
||||||
|
}
|
||||||
|
if (match(TokenType.IF)) {
|
||||||
|
pattern.optCondition = expression();
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TokenType.COLON);
|
||||||
|
if (lookMatch(0, TokenType.LBRACE)) {
|
||||||
|
pattern.result = block();
|
||||||
|
} else {
|
||||||
|
pattern.result = new ReturnStatement(expression());
|
||||||
|
}
|
||||||
|
patterns.add(pattern);
|
||||||
|
} while (!match(TokenType.RBRACE));
|
||||||
|
|
||||||
|
return new MatchExpression(expression, patterns);
|
||||||
|
}
|
||||||
|
|
||||||
private Expression expression() {
|
private Expression expression() {
|
||||||
return ternary();
|
return ternary();
|
||||||
}
|
}
|
||||||
@ -459,13 +508,40 @@ public final class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Expression primary() {
|
private Expression primary() {
|
||||||
|
if (match(TokenType.LPAREN)) {
|
||||||
|
Expression result = expression();
|
||||||
|
match(TokenType.RPAREN);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match(TokenType.COLONCOLON)) {
|
||||||
|
final String functionName = consume(TokenType.WORD).getText();
|
||||||
|
return new FunctionReferenceExpression(functionName);
|
||||||
|
}
|
||||||
|
if (match(TokenType.MATCH)) {
|
||||||
|
return match();
|
||||||
|
}
|
||||||
|
if (match(TokenType.DEF)) {
|
||||||
|
consume(TokenType.LPAREN);
|
||||||
|
final List<String> argNames = new ArrayList<>();
|
||||||
|
while (!match(TokenType.RPAREN)) {
|
||||||
|
argNames.add(consume(TokenType.WORD).getText());
|
||||||
|
match(TokenType.COMMA);
|
||||||
|
}
|
||||||
|
Statement statement;
|
||||||
|
if (lookMatch(0, TokenType.EQ)) {
|
||||||
|
match(TokenType.EQ);
|
||||||
|
statement = new ReturnStatement(expression());
|
||||||
|
} else {
|
||||||
|
statement = statementOrBlock();
|
||||||
|
}
|
||||||
|
return new ValueExpression(new UserDefinedFunction(argNames, statement));
|
||||||
|
}
|
||||||
|
return variable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression variable() {
|
||||||
final Token current = get(0);
|
final Token current = get(0);
|
||||||
if (match(TokenType.NUMBER)) {
|
|
||||||
return new ValueExpression(Double.parseDouble(current.getText()));
|
|
||||||
}
|
|
||||||
if (match(TokenType.HEX_NUMBER)) {
|
|
||||||
return new ValueExpression(Long.parseLong(current.getText(), 16));
|
|
||||||
}
|
|
||||||
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) {
|
if (lookMatch(0, TokenType.WORD) && lookMatch(1, TokenType.LBRACKET)) {
|
||||||
return element();
|
return element();
|
||||||
}
|
}
|
||||||
@ -484,34 +560,20 @@ public final class Parser {
|
|||||||
if (match(TokenType.WORD)) {
|
if (match(TokenType.WORD)) {
|
||||||
return new VariableExpression(current.getText());
|
return new VariableExpression(current.getText());
|
||||||
}
|
}
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression value() {
|
||||||
|
final Token current = get(0);
|
||||||
|
if (match(TokenType.NUMBER)) {
|
||||||
|
return new ValueExpression(Double.parseDouble(current.getText()));
|
||||||
|
}
|
||||||
|
if (match(TokenType.HEX_NUMBER)) {
|
||||||
|
return new ValueExpression(Long.parseLong(current.getText(), 16));
|
||||||
|
}
|
||||||
if (match(TokenType.TEXT)) {
|
if (match(TokenType.TEXT)) {
|
||||||
return new ValueExpression(current.getText());
|
return new ValueExpression(current.getText());
|
||||||
}
|
}
|
||||||
if (match(TokenType.COLONCOLON)) {
|
|
||||||
final String functionName = consume(TokenType.WORD).getText();
|
|
||||||
return new FunctionReferenceExpression(functionName);
|
|
||||||
}
|
|
||||||
if (match(TokenType.DEF)) {
|
|
||||||
consume(TokenType.LPAREN);
|
|
||||||
final List<String> argNames = new ArrayList<>();
|
|
||||||
while (!match(TokenType.RPAREN)) {
|
|
||||||
argNames.add(consume(TokenType.WORD).getText());
|
|
||||||
match(TokenType.COMMA);
|
|
||||||
}
|
|
||||||
Statement statement;
|
|
||||||
if (lookMatch(0, TokenType.EQ)) {
|
|
||||||
match(TokenType.EQ);
|
|
||||||
statement = new ReturnStatement(expression());
|
|
||||||
} else {
|
|
||||||
statement = statementOrBlock();
|
|
||||||
}
|
|
||||||
return new ValueExpression(new UserDefinedFunction(argNames, statement));
|
|
||||||
}
|
|
||||||
if (match(TokenType.LPAREN)) {
|
|
||||||
Expression result = expression();
|
|
||||||
match(TokenType.RPAREN);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
throw new ParseException("Unknown expression: " + current);
|
throw new ParseException("Unknown expression: " + current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ public enum TokenType {
|
|||||||
DEF,
|
DEF,
|
||||||
RETURN,
|
RETURN,
|
||||||
USE,
|
USE,
|
||||||
|
MATCH,
|
||||||
|
CASE,
|
||||||
|
|
||||||
PLUS, // +
|
PLUS, // +
|
||||||
MINUS, // -
|
MINUS, // -
|
||||||
|
119
src/com/annimon/ownlang/parser/ast/MatchExpression.java
Normal file
119
src/com/annimon/ownlang/parser/ast/MatchExpression.java
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.NumberValue;
|
||||||
|
import com.annimon.ownlang.lib.Value;
|
||||||
|
import com.annimon.ownlang.lib.Variables;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class MatchExpression implements Expression {
|
||||||
|
|
||||||
|
public final Expression expression;
|
||||||
|
public final List<Pattern> patterns;
|
||||||
|
|
||||||
|
public MatchExpression(Expression expression, List<Pattern> patterns) {
|
||||||
|
this.expression = expression;
|
||||||
|
this.patterns = patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value eval() {
|
||||||
|
final Value value = expression.eval();
|
||||||
|
for (Pattern p : patterns) {
|
||||||
|
if (p instanceof ConstantPattern) {
|
||||||
|
final ConstantPattern pattern = (ConstantPattern) p;
|
||||||
|
if (match(value, pattern.constant) && optMatches(p)) {
|
||||||
|
return evalResult(p.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p instanceof VariablePattern) {
|
||||||
|
final VariablePattern pattern = (VariablePattern) p;
|
||||||
|
if (pattern.variable.equals("_")) return evalResult(p.result);
|
||||||
|
|
||||||
|
if (Variables.isExists(pattern.variable)) {
|
||||||
|
if (match(value, Variables.get(pattern.variable)) && optMatches(p)) {
|
||||||
|
return evalResult(p.result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Variables.set(pattern.variable, value);
|
||||||
|
if (optMatches(p)) {
|
||||||
|
final Value result = evalResult(p.result);;
|
||||||
|
Variables.remove(pattern.variable);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
Variables.remove(pattern.variable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException("No pattern were matched");
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean match(Value value, Value constant) {
|
||||||
|
if (value.type() != constant.type()) return false;
|
||||||
|
return value.equals(constant);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean optMatches(Pattern pattern) {
|
||||||
|
if (pattern.optCondition == null) return true;
|
||||||
|
return pattern.optCondition.eval() != NumberValue.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Value evalResult(Statement s) {
|
||||||
|
try {
|
||||||
|
s.execute();
|
||||||
|
} catch (ReturnStatement ret) {
|
||||||
|
return ret.getResult();
|
||||||
|
}
|
||||||
|
return NumberValue.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(Visitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("match ").append(expression).append(" {");
|
||||||
|
for (Pattern p : patterns) {
|
||||||
|
sb.append("\n case ").append(p);
|
||||||
|
}
|
||||||
|
sb.append("\n}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class Pattern {
|
||||||
|
public Statement result;
|
||||||
|
public Expression optCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ConstantPattern extends Pattern {
|
||||||
|
public Value constant;
|
||||||
|
|
||||||
|
public ConstantPattern(Value pattern) {
|
||||||
|
this.constant = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return constant + ": " + result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class VariablePattern extends Pattern {
|
||||||
|
public String variable;
|
||||||
|
|
||||||
|
public VariablePattern(String pattern) {
|
||||||
|
this.variable = pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return variable + ": " + result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ public interface Visitor {
|
|||||||
void visit(FunctionalExpression s);
|
void visit(FunctionalExpression s);
|
||||||
void visit(IfStatement s);
|
void visit(IfStatement s);
|
||||||
void visit(MapExpression s);
|
void visit(MapExpression s);
|
||||||
|
void visit(MatchExpression s);
|
||||||
void visit(PrintStatement s);
|
void visit(PrintStatement s);
|
||||||
void visit(PrintlnStatement s);
|
void visit(PrintlnStatement s);
|
||||||
void visit(ReturnStatement s);
|
void visit(ReturnStatement s);
|
||||||
|
@ -126,6 +126,11 @@ public abstract class AbstractVisitor implements Visitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(MatchExpression s) {
|
||||||
|
s.expression.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(PrintStatement s) {
|
public void visit(PrintStatement s) {
|
||||||
s.expression.accept(this);
|
s.expression.accept(this);
|
||||||
|
Loading…
Reference in New Issue
Block a user