diff --git a/src/ast_parser/BinaryOperatorExpr.java b/src/ast_parser/BinaryOperatorExpr.java new file mode 100644 index 0000000..ce75ae8 --- /dev/null +++ b/src/ast_parser/BinaryOperatorExpr.java @@ -0,0 +1,24 @@ +package ast_parser; + +class BinaryOperatorExpr extends Expr { + Expr left; + Expr right; + char operator; + + BinaryOperatorExpr(Expr left, Expr right, char operator) { + this.left = left; + this.right = right; + this.operator = operator; + } + + @Override + double evaluate() { + return switch (operator) { + case '+' -> left.evaluate() + right.evaluate(); + case '-' -> left.evaluate() - right.evaluate(); + case '*' -> left.evaluate() * right.evaluate(); + case '/' -> left.evaluate() / right.evaluate(); + default -> throw new RuntimeException("Unsupported operator: " + operator); + }; + } +} diff --git a/src/ast_parser/Expr.java b/src/ast_parser/Expr.java new file mode 100644 index 0000000..d69a6ba --- /dev/null +++ b/src/ast_parser/Expr.java @@ -0,0 +1,5 @@ +package ast_parser; + +abstract class Expr { + abstract double evaluate(); +} diff --git a/src/ast_parser/FunctionExpr.java b/src/ast_parser/FunctionExpr.java new file mode 100644 index 0000000..f221a34 --- /dev/null +++ b/src/ast_parser/FunctionExpr.java @@ -0,0 +1,50 @@ +package ast_parser; + +import java.util.List; + +class FunctionExpr extends Expr { + String function; + List arguments; + + FunctionExpr(String function, List arguments) { + this.function = function; + this.arguments = arguments; + } + + @Override + double evaluate() { + return switch (function) { + case "sqrt" -> { + if (arguments.size() != 1) { + throw new RuntimeException("sqrt expects exactly one argument"); + } + yield Math.sqrt(arguments.get(0).evaluate()); + } + case "logtwo" -> { + if (arguments.size() != 1) { + throw new RuntimeException("log2 expects exactly one argument"); + } + yield Math.log(arguments.get(0).evaluate()) / Math.log(2); + } + case "logten" -> { + if (arguments.size() != 1) { + throw new RuntimeException("log10 expects exactly one argument"); + } + yield Math.log10(arguments.get(0).evaluate()); + } + case "loge" -> { + if (arguments.size() != 1) { + throw new RuntimeException("loge expects exactly one argument"); + } + yield Math.log(arguments.get(0).evaluate()); + } + case "powtwo" -> { + if (arguments.size() != 1) { + throw new RuntimeException("pow2 expects exactly one argument"); + } + yield Math.pow(2, arguments.get(0).evaluate()); + } + default -> throw new RuntimeException("Unsupported function or wrong number of arguments: " + function); + }; + } +} diff --git a/src/ast_parser/Lexer.java b/src/ast_parser/Lexer.java new file mode 100644 index 0000000..84519ab --- /dev/null +++ b/src/ast_parser/Lexer.java @@ -0,0 +1,51 @@ +package ast_parser; + +import java.util.ArrayList; +import java.util.List; + +public class Lexer { + public static List tokenize(String input) { + List tokens = new ArrayList<>(); + input = input.replaceAll("\\s", ""); + int i = 0; + while (i < input.length()) { + char ch = input.charAt(i); + if (Character.isDigit(ch)) { + StringBuilder sb = new StringBuilder(); + while (i < input.length() && (Character.isDigit(input.charAt(i)) || input.charAt(i) == '.')) { + sb.append(input.charAt(i)); + i++; + } + tokens.add(new Token(TokenType.NUMBER, sb.toString())); + } else if (Character.isLetter(ch)) { + StringBuilder sb = new StringBuilder(); + while (i < input.length() && Character.isLetter(input.charAt(i))) { + sb.append(input.charAt(i)); + i++; + } + tokens.add(new Token(TokenType.FUNCTION, sb.toString())); + } else { + switch (ch) { + case '(': + tokens.add(new Token(TokenType.LPAREN, "(")); + i++; + break; + case ')': + tokens.add(new Token(TokenType.RPAREN, ")")); + i++; + break; + case ',': + tokens.add(new Token(TokenType.COMMA, ",")); + i++; + break; + default: + tokens.add(new Token(TokenType.OPERATOR, String.valueOf(ch))); + i++; + break; + } + } + } + tokens.add(new Token(TokenType.EOF, "")); + return tokens; + } +} diff --git a/src/ast_parser/MainParser.java b/src/ast_parser/MainParser.java new file mode 100644 index 0000000..2ea4f28 --- /dev/null +++ b/src/ast_parser/MainParser.java @@ -0,0 +1,17 @@ +package ast_parser; + +import java.util.List; + +public class MainParser { + public static void main(String[] args) { + String input = "(3 + 5) * sqrt(16) + powtwo(5)"; + List tokens = Lexer.tokenize(input); + System.out.println("Tokens:"); + tokens.forEach(System.out::println); + + Parser parser = new Parser(tokens); + Expr expression = parser.parse(); + double result = expression.evaluate(); + System.out.println("Result: " + result); + } +} diff --git a/src/ast_parser/NumberExpr.java b/src/ast_parser/NumberExpr.java new file mode 100644 index 0000000..28bb7fa --- /dev/null +++ b/src/ast_parser/NumberExpr.java @@ -0,0 +1,14 @@ +package ast_parser; + +class NumberExpr extends Expr { + double value; + + NumberExpr(double value) { + this.value = value; + } + + @Override + double evaluate() { + return value; + } +} diff --git a/src/ast_parser/Parser.java b/src/ast_parser/Parser.java new file mode 100644 index 0000000..6831f5f --- /dev/null +++ b/src/ast_parser/Parser.java @@ -0,0 +1,78 @@ +package ast_parser; + +import java.util.ArrayList; +import java.util.List; + +public class Parser { + private final List tokens; + private int current = 0; + + public Parser(List tokens) { + this.tokens = tokens; + } + + public Expr parse() { + return expression(); + } + + private Expr expression() { + Expr expr = term(); + while (match(TokenType.OPERATOR)) { + char operator = tokens.get(current - 1).value.charAt(0); + Expr right = term(); + expr = new BinaryOperatorExpr(expr, right, operator); + } + return expr; + } + + private Expr term() { + if (match(TokenType.NUMBER)) { + return new NumberExpr(Double.parseDouble(previous().value)); + } else if (match(TokenType.LPAREN)) { + Expr expr = expression(); + consume(TokenType.RPAREN, "Expect ')' after expression."); + return expr; + } else if (match(TokenType.FUNCTION)) { + String functionName = previous().value; + consume(TokenType.LPAREN, "Expect '(' after function name."); + List args = new ArrayList<>(); + if (!check(TokenType.RPAREN)) { + do { + args.add(expression()); + } while (match(TokenType.COMMA)); + } + consume(TokenType.RPAREN, "Expect ')' after arguments."); + return new FunctionExpr(functionName, args); + } + throw new RuntimeException("Unexpected token."); + } + + private boolean match(TokenType type) { + if (check(type)) { + current++; + return true; + } + return false; + } + + private boolean check(TokenType type) { + if (isAtEnd()) return false; + return tokens.get(current).type == type; + } + + private Token previous() { + return tokens.get(current - 1); + } + + private void consume(TokenType type, String message) { + if (check(type)) { + current++; + return; + } + throw new RuntimeException(message); + } + + private boolean isAtEnd() { + return current >= tokens.size() || tokens.get(current).type == TokenType.EOF; + } +} diff --git a/src/ast_parser/Token.java b/src/ast_parser/Token.java new file mode 100644 index 0000000..89faaad --- /dev/null +++ b/src/ast_parser/Token.java @@ -0,0 +1,16 @@ +package ast_parser; + +public class Token { + public TokenType type; + public String value; + + public Token(TokenType type, String value) { + this.type = type; + this.value = value; + } + + @Override + public String toString() { + return type + " : " + value; + } +} diff --git a/src/ast_parser/TokenType.java b/src/ast_parser/TokenType.java new file mode 100644 index 0000000..fdb715b --- /dev/null +++ b/src/ast_parser/TokenType.java @@ -0,0 +1,11 @@ +package ast_parser; + +public enum TokenType { + NUMBER, + OPERATOR, + LPAREN, + RPAREN, + FUNCTION, + COMMA, + EOF +} diff --git a/src/reports/lab_five.pdf b/src/reports/lab_five.pdf new file mode 100644 index 0000000..c354dec Binary files /dev/null and b/src/reports/lab_five.pdf differ