Урок 4. Строки, оператор print

This commit is contained in:
Victor 2015-05-25 23:20:10 +03:00
parent 8a440f00d8
commit c842dd0de3
16 changed files with 223 additions and 36 deletions

6
program.txt Normal file
View 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

View File

@ -5,6 +5,9 @@ import com.annimon.ownlang.parser.Lexer;
import com.annimon.ownlang.parser.Parser;
import com.annimon.ownlang.parser.Token;
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;
/**
@ -12,11 +15,9 @@ import java.util.List;
*/
public final class Main {
public static void main(String[] args) {
final String input1 = "word = 2 + 2\nword2 = PI + word";
// final String input2 = "(GOLDEN_RATIO + 2) * #f";
final String input2 = "GOLDEN_RATIO";
final List<Token> tokens = new Lexer(input1).tokenize();
public static void main(String[] args) throws IOException {
final String input = new String( Files.readAllBytes(Paths.get("program.txt")), "UTF-8");
final List<Token> tokens = new Lexer(input).tokenize();
for (Token token : tokens) {
System.out.println(token);
}
@ -28,7 +29,5 @@ public final class Main {
for (Statement statement : statements) {
statement.execute();
}
System.out.printf("%s = %f\n", "word", Variables.get("word"));
System.out.printf("%s = %f\n", "word2", Variables.get("word2"));
}
}

View File

@ -1,24 +1,29 @@
package com.annimon.ownlang.parser.ast;
package com.annimon.ownlang.lib;
/**
*
* @author aNNiMON
*/
public final class NumberExpression implements Expression {
public final class NumberValue implements Value {
private final double value;
public NumberExpression(double value) {
public NumberValue(double value) {
this.value = value;
}
@Override
public double eval() {
public double asNumber() {
return value;
}
@Override
public String toString() {
public String asString() {
return Double.toString(value);
}
@Override
public String toString() {
return asString();
}
}

View 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();
}
}

View File

@ -0,0 +1,12 @@
package com.annimon.ownlang.lib;
/**
*
* @author aNNiMON
*/
public interface Value {
double asNumber();
String asString();
}

View File

@ -9,26 +9,27 @@ import java.util.Map;
*/
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 {
variables = new HashMap<>();
variables.put("PI", Math.PI);
variables.put("ПИ", Math.PI);
variables.put("E", Math.E);
variables.put("GOLDEN_RATIO", 1.618);
variables.put("PI", new NumberValue(Math.PI));
variables.put("ПИ", new NumberValue(Math.PI));
variables.put("E", new NumberValue(Math.E));
variables.put("GOLDEN_RATIO", new NumberValue(1.618));
}
public static boolean isExists(String key) {
return variables.containsKey(key);
}
public static double get(String key) {
if (!isExists(key)) return 0;
public static Value get(String key) {
if (!isExists(key)) return ZERO;
return variables.get(key);
}
public static void set(String key, double value) {
public static void set(String key, Value value) {
variables.put(key, value);
}
}

View File

@ -36,6 +36,7 @@ public final class Lexer {
final char current = peek(0);
if (Character.isDigit(current)) tokenizeNumber();
else if (Character.isLetter(current)) tokenizeWord();
else if (current == '"') tokenizeText();
else if (current == '#') {
next();
tokenizeHexNumber();
@ -95,7 +96,37 @@ public final class Lexer {
buffer.append(current);
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() {

View File

@ -1,10 +1,11 @@
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.BinaryExpression;
import com.annimon.ownlang.parser.ast.VariabletExpression;
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.UnaryExpression;
import java.util.ArrayList;
@ -37,6 +38,9 @@ public final class Parser {
}
private Statement statement() {
if (match(TokenType.PRINT)) {
return new PrintStatement(expression());
}
return assignmentStatement();
}
@ -107,14 +111,17 @@ public final class Parser {
private Expression primary() {
final Token current = get(0);
if (match(TokenType.NUMBER)) {
return new NumberExpression(Double.parseDouble(current.getText()));
return new ValueExpression(Double.parseDouble(current.getText()));
}
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)) {
return new VariabletExpression(current.getText());
}
if (match(TokenType.TEXT)) {
return new ValueExpression(current.getText());
}
if (match(TokenType.LPAREN)) {
Expression result = expression();
match(TokenType.RPAREN);

View File

@ -9,6 +9,10 @@ public enum TokenType {
NUMBER,
HEX_NUMBER,
WORD,
TEXT,
// keyword
PRINT,
PLUS,
MINUS,

View File

@ -1,5 +1,6 @@
package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.lib.Variables;
/**
@ -18,7 +19,7 @@ public final class AssignmentStatement implements Statement {
@Override
public void execute() {
final double result = expression.eval();
final Value result = expression.eval();
Variables.set(variable, result);
}

View File

@ -1,5 +1,9 @@
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
@ -16,14 +20,35 @@ public final class BinaryExpression implements Expression {
}
@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) {
case '*': {
final int iterations = (int) value2.asNumber();
final StringBuilder buffer = new StringBuilder();
for (int i = 0; i < iterations; i++) {
buffer.append(string1);
}
return new StringValue(buffer.toString());
}
case '+':
default:
return new StringValue(string1 + value2.asString());
}
}
final double number1 = value1.asNumber();
final double number2 = value2.asNumber();
switch (operation) {
case '-': return expr1.eval() - expr2.eval();
case '*': return expr1.eval() * expr2.eval();
case '/': return expr1.eval() / expr2.eval();
case '-': return new NumberValue(number1 - number2);
case '*': return new NumberValue(number1 * number2);
case '/': return new NumberValue(number1 / number2);
case '+':
default:
return expr1.eval() + expr2.eval();
return new NumberValue(number1 + number2);
}
}

View File

@ -1,10 +1,12 @@
package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.Value;
/**
*
* @author aNNiMON
*/
public interface Expression {
double eval();
Value eval();
}

View 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;
}
}

View File

@ -1,5 +1,8 @@
package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.NumberValue;
import com.annimon.ownlang.lib.Value;
/**
*
* @author aNNiMON
@ -15,9 +18,9 @@ public final class UnaryExpression implements Expression {
}
@Override
public double eval() {
public Value eval() {
switch (operation) {
case '-': return -expr1.eval();
case '-': return new NumberValue(-expr1.eval().asNumber());
case '+':
default:
return expr1.eval();

View 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();
}
}

View File

@ -1,5 +1,6 @@
package com.annimon.ownlang.parser.ast;
import com.annimon.ownlang.lib.Value;
import com.annimon.ownlang.lib.Variables;
/**
@ -15,7 +16,7 @@ public final class VariabletExpression implements Expression {
}
@Override
public double eval() {
public Value eval() {
if (!Variables.isExists(name)) throw new RuntimeException("Constant does not exists");
return Variables.get(name);
}