mirror of
https://github.com/aNNiMON/Own-Programming-Language-Tutorial.git
synced 2024-09-20 00:34:20 +03:00
Урок 4. Строки, оператор print
This commit is contained in:
parent
8a440f00d8
commit
c842dd0de3
6
program.txt
Normal file
6
program.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
word = 2 + 2
|
||||||
|
word2 = PI + word
|
||||||
|
str = "a" * 5 + "ba" * 7 + "\n"
|
||||||
|
print str * "3" * 2
|
||||||
|
print "word = " + word + "\n"
|
||||||
|
print "word2 = " + word2
|
@ -5,6 +5,9 @@ import com.annimon.ownlang.parser.Lexer;
|
|||||||
import com.annimon.ownlang.parser.Parser;
|
import com.annimon.ownlang.parser.Parser;
|
||||||
import com.annimon.ownlang.parser.Token;
|
import com.annimon.ownlang.parser.Token;
|
||||||
import com.annimon.ownlang.parser.ast.Statement;
|
import com.annimon.ownlang.parser.ast.Statement;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -12,11 +15,9 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public final class Main {
|
public final class Main {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) throws IOException {
|
||||||
final String input1 = "word = 2 + 2\nword2 = PI + word";
|
final String input = new String( Files.readAllBytes(Paths.get("program.txt")), "UTF-8");
|
||||||
// final String input2 = "(GOLDEN_RATIO + 2) * #f";
|
final List<Token> tokens = new Lexer(input).tokenize();
|
||||||
final String input2 = "GOLDEN_RATIO";
|
|
||||||
final List<Token> tokens = new Lexer(input1).tokenize();
|
|
||||||
for (Token token : tokens) {
|
for (Token token : tokens) {
|
||||||
System.out.println(token);
|
System.out.println(token);
|
||||||
}
|
}
|
||||||
@ -28,7 +29,5 @@ public final class Main {
|
|||||||
for (Statement statement : statements) {
|
for (Statement statement : statements) {
|
||||||
statement.execute();
|
statement.execute();
|
||||||
}
|
}
|
||||||
System.out.printf("%s = %f\n", "word", Variables.get("word"));
|
|
||||||
System.out.printf("%s = %f\n", "word2", Variables.get("word2"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,29 @@
|
|||||||
package com.annimon.ownlang.parser.ast;
|
package com.annimon.ownlang.lib;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author aNNiMON
|
* @author aNNiMON
|
||||||
*/
|
*/
|
||||||
public final class NumberExpression implements Expression {
|
public final class NumberValue implements Value {
|
||||||
|
|
||||||
private final double value;
|
private final double value;
|
||||||
|
|
||||||
public NumberExpression(double value) {
|
public NumberValue(double value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double eval() {
|
public double asNumber() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String asString() {
|
||||||
return Double.toString(value);
|
return Double.toString(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return asString();
|
||||||
|
}
|
||||||
}
|
}
|
34
src/com/annimon/ownlang/lib/StringValue.java
Normal file
34
src/com/annimon/ownlang/lib/StringValue.java
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package com.annimon.ownlang.lib;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class StringValue implements Value {
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public StringValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double asNumber() {
|
||||||
|
try {
|
||||||
|
return Double.parseDouble(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String asString() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return asString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
src/com/annimon/ownlang/lib/Value.java
Normal file
12
src/com/annimon/ownlang/lib/Value.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.annimon.ownlang.lib;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public interface Value {
|
||||||
|
|
||||||
|
double asNumber();
|
||||||
|
|
||||||
|
String asString();
|
||||||
|
}
|
@ -9,26 +9,27 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public final class Variables {
|
public final class Variables {
|
||||||
|
|
||||||
private static final Map<String, Double> variables;
|
private static final NumberValue ZERO = new NumberValue(0);
|
||||||
|
private static final Map<String, Value> variables;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
variables = new HashMap<>();
|
variables = new HashMap<>();
|
||||||
variables.put("PI", Math.PI);
|
variables.put("PI", new NumberValue(Math.PI));
|
||||||
variables.put("ПИ", Math.PI);
|
variables.put("ПИ", new NumberValue(Math.PI));
|
||||||
variables.put("E", Math.E);
|
variables.put("E", new NumberValue(Math.E));
|
||||||
variables.put("GOLDEN_RATIO", 1.618);
|
variables.put("GOLDEN_RATIO", new NumberValue(1.618));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isExists(String key) {
|
public static boolean isExists(String key) {
|
||||||
return variables.containsKey(key);
|
return variables.containsKey(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static double get(String key) {
|
public static Value get(String key) {
|
||||||
if (!isExists(key)) return 0;
|
if (!isExists(key)) return ZERO;
|
||||||
return variables.get(key);
|
return variables.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void set(String key, double value) {
|
public static void set(String key, Value value) {
|
||||||
variables.put(key, value);
|
variables.put(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ public final class Lexer {
|
|||||||
final char current = peek(0);
|
final char current = peek(0);
|
||||||
if (Character.isDigit(current)) tokenizeNumber();
|
if (Character.isDigit(current)) tokenizeNumber();
|
||||||
else if (Character.isLetter(current)) tokenizeWord();
|
else if (Character.isLetter(current)) tokenizeWord();
|
||||||
|
else if (current == '"') tokenizeText();
|
||||||
else if (current == '#') {
|
else if (current == '#') {
|
||||||
next();
|
next();
|
||||||
tokenizeHexNumber();
|
tokenizeHexNumber();
|
||||||
@ -95,7 +96,37 @@ public final class Lexer {
|
|||||||
buffer.append(current);
|
buffer.append(current);
|
||||||
current = next();
|
current = next();
|
||||||
}
|
}
|
||||||
addToken(TokenType.WORD, buffer.toString());
|
|
||||||
|
final String word = buffer.toString();
|
||||||
|
if (word.equals("print")) {
|
||||||
|
addToken(TokenType.PRINT);
|
||||||
|
} else {
|
||||||
|
addToken(TokenType.WORD, word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tokenizeText() {
|
||||||
|
next();// skip "
|
||||||
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
char current = peek(0);
|
||||||
|
while (true) {
|
||||||
|
if (current == '\\') {
|
||||||
|
current = next();
|
||||||
|
switch (current) {
|
||||||
|
case '"': current = next(); buffer.append('"'); continue;
|
||||||
|
case 'n': current = next(); buffer.append('\n'); continue;
|
||||||
|
case 't': current = next(); buffer.append('\t'); continue;
|
||||||
|
}
|
||||||
|
buffer.append('\\');
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (current == '"') break;
|
||||||
|
buffer.append(current);
|
||||||
|
current = next();
|
||||||
|
}
|
||||||
|
next(); // skip closing "
|
||||||
|
|
||||||
|
addToken(TokenType.TEXT, buffer.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private char next() {
|
private char next() {
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package com.annimon.ownlang.parser;
|
package com.annimon.ownlang.parser;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.parser.ast.PrintStatement;
|
||||||
import com.annimon.ownlang.parser.ast.AssignmentStatement;
|
import com.annimon.ownlang.parser.ast.AssignmentStatement;
|
||||||
import com.annimon.ownlang.parser.ast.BinaryExpression;
|
import com.annimon.ownlang.parser.ast.BinaryExpression;
|
||||||
import com.annimon.ownlang.parser.ast.VariabletExpression;
|
import com.annimon.ownlang.parser.ast.VariabletExpression;
|
||||||
import com.annimon.ownlang.parser.ast.Expression;
|
import com.annimon.ownlang.parser.ast.Expression;
|
||||||
import com.annimon.ownlang.parser.ast.NumberExpression;
|
import com.annimon.ownlang.parser.ast.ValueExpression;
|
||||||
import com.annimon.ownlang.parser.ast.Statement;
|
import com.annimon.ownlang.parser.ast.Statement;
|
||||||
import com.annimon.ownlang.parser.ast.UnaryExpression;
|
import com.annimon.ownlang.parser.ast.UnaryExpression;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -37,6 +38,9 @@ public final class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Statement statement() {
|
private Statement statement() {
|
||||||
|
if (match(TokenType.PRINT)) {
|
||||||
|
return new PrintStatement(expression());
|
||||||
|
}
|
||||||
return assignmentStatement();
|
return assignmentStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,14 +111,17 @@ public final class Parser {
|
|||||||
private Expression primary() {
|
private Expression primary() {
|
||||||
final Token current = get(0);
|
final Token current = get(0);
|
||||||
if (match(TokenType.NUMBER)) {
|
if (match(TokenType.NUMBER)) {
|
||||||
return new NumberExpression(Double.parseDouble(current.getText()));
|
return new ValueExpression(Double.parseDouble(current.getText()));
|
||||||
}
|
}
|
||||||
if (match(TokenType.HEX_NUMBER)) {
|
if (match(TokenType.HEX_NUMBER)) {
|
||||||
return new NumberExpression(Long.parseLong(current.getText(), 16));
|
return new ValueExpression(Long.parseLong(current.getText(), 16));
|
||||||
}
|
}
|
||||||
if (match(TokenType.WORD)) {
|
if (match(TokenType.WORD)) {
|
||||||
return new VariabletExpression(current.getText());
|
return new VariabletExpression(current.getText());
|
||||||
}
|
}
|
||||||
|
if (match(TokenType.TEXT)) {
|
||||||
|
return new ValueExpression(current.getText());
|
||||||
|
}
|
||||||
if (match(TokenType.LPAREN)) {
|
if (match(TokenType.LPAREN)) {
|
||||||
Expression result = expression();
|
Expression result = expression();
|
||||||
match(TokenType.RPAREN);
|
match(TokenType.RPAREN);
|
||||||
|
@ -9,6 +9,10 @@ public enum TokenType {
|
|||||||
NUMBER,
|
NUMBER,
|
||||||
HEX_NUMBER,
|
HEX_NUMBER,
|
||||||
WORD,
|
WORD,
|
||||||
|
TEXT,
|
||||||
|
|
||||||
|
// keyword
|
||||||
|
PRINT,
|
||||||
|
|
||||||
PLUS,
|
PLUS,
|
||||||
MINUS,
|
MINUS,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.annimon.ownlang.parser.ast;
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.Value;
|
||||||
import com.annimon.ownlang.lib.Variables;
|
import com.annimon.ownlang.lib.Variables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -18,7 +19,7 @@ public final class AssignmentStatement implements Statement {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
final double result = expression.eval();
|
final Value result = expression.eval();
|
||||||
Variables.set(variable, result);
|
Variables.set(variable, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package com.annimon.ownlang.parser.ast;
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.NumberValue;
|
||||||
|
import com.annimon.ownlang.lib.StringValue;
|
||||||
|
import com.annimon.ownlang.lib.Value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author aNNiMON
|
* @author aNNiMON
|
||||||
@ -16,14 +20,35 @@ public final class BinaryExpression implements Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double eval() {
|
public Value eval() {
|
||||||
|
final Value value1 = expr1.eval();
|
||||||
|
final Value value2 = expr2.eval();
|
||||||
|
if (value1 instanceof StringValue) {
|
||||||
|
final String string1 = value1.asString();
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case '-': return expr1.eval() - expr2.eval();
|
case '*': {
|
||||||
case '*': return expr1.eval() * expr2.eval();
|
final int iterations = (int) value2.asNumber();
|
||||||
case '/': return expr1.eval() / expr2.eval();
|
final StringBuilder buffer = new StringBuilder();
|
||||||
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
buffer.append(string1);
|
||||||
|
}
|
||||||
|
return new StringValue(buffer.toString());
|
||||||
|
}
|
||||||
case '+':
|
case '+':
|
||||||
default:
|
default:
|
||||||
return expr1.eval() + expr2.eval();
|
return new StringValue(string1 + value2.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final double number1 = value1.asNumber();
|
||||||
|
final double number2 = value2.asNumber();
|
||||||
|
switch (operation) {
|
||||||
|
case '-': return new NumberValue(number1 - number2);
|
||||||
|
case '*': return new NumberValue(number1 * number2);
|
||||||
|
case '/': return new NumberValue(number1 / number2);
|
||||||
|
case '+':
|
||||||
|
default:
|
||||||
|
return new NumberValue(number1 + number2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package com.annimon.ownlang.parser.ast;
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.Value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author aNNiMON
|
* @author aNNiMON
|
||||||
*/
|
*/
|
||||||
public interface Expression {
|
public interface Expression {
|
||||||
|
|
||||||
double eval();
|
Value eval();
|
||||||
}
|
}
|
||||||
|
24
src/com/annimon/ownlang/parser/ast/PrintStatement.java
Normal file
24
src/com/annimon/ownlang/parser/ast/PrintStatement.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class PrintStatement implements Statement {
|
||||||
|
|
||||||
|
private final Expression expression;
|
||||||
|
|
||||||
|
public PrintStatement(Expression expression) {
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() {
|
||||||
|
System.out.print(expression.eval());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "print " + expression;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
package com.annimon.ownlang.parser.ast;
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.NumberValue;
|
||||||
|
import com.annimon.ownlang.lib.Value;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author aNNiMON
|
* @author aNNiMON
|
||||||
@ -15,9 +18,9 @@ public final class UnaryExpression implements Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double eval() {
|
public Value eval() {
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case '-': return -expr1.eval();
|
case '-': return new NumberValue(-expr1.eval().asNumber());
|
||||||
case '+':
|
case '+':
|
||||||
default:
|
default:
|
||||||
return expr1.eval();
|
return expr1.eval();
|
||||||
|
32
src/com/annimon/ownlang/parser/ast/ValueExpression.java
Normal file
32
src/com/annimon/ownlang/parser/ast/ValueExpression.java
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.NumberValue;
|
||||||
|
import com.annimon.ownlang.lib.StringValue;
|
||||||
|
import com.annimon.ownlang.lib.Value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author aNNiMON
|
||||||
|
*/
|
||||||
|
public final class ValueExpression implements Expression {
|
||||||
|
|
||||||
|
private final Value value;
|
||||||
|
|
||||||
|
public ValueExpression(double value) {
|
||||||
|
this.value = new NumberValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueExpression(String value) {
|
||||||
|
this.value = new StringValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value eval() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return value.asString();
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package com.annimon.ownlang.parser.ast;
|
package com.annimon.ownlang.parser.ast;
|
||||||
|
|
||||||
|
import com.annimon.ownlang.lib.Value;
|
||||||
import com.annimon.ownlang.lib.Variables;
|
import com.annimon.ownlang.lib.Variables;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,7 +16,7 @@ public final class VariabletExpression implements Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double eval() {
|
public Value eval() {
|
||||||
if (!Variables.isExists(name)) throw new RuntimeException("Constant does not exists");
|
if (!Variables.isExists(name)) throw new RuntimeException("Constant does not exists");
|
||||||
return Variables.get(name);
|
return Variables.get(name);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user