diff --git a/src/main/java/com/github/nilstrieb/hunterlang/hllibrary/FunctionArgLookup.java b/src/main/java/com/github/nilstrieb/hunterlang/hllibrary/FunctionArgLookup.java index 870ab37..1098b5c 100644 --- a/src/main/java/com/github/nilstrieb/hunterlang/hllibrary/FunctionArgLookup.java +++ b/src/main/java/com/github/nilstrieb/hunterlang/hllibrary/FunctionArgLookup.java @@ -2,13 +2,10 @@ package com.github.nilstrieb.hunterlang.hllibrary; public class FunctionArgLookup { - public static int argLookup(String lib, String name){ - return switch (lib){ - case "Leorio" -> switch (name){ - case "say" -> 1; - case "listen" -> 0; - default -> 0; - }; + public static int argLookup(String name) { + return switch (name) { + case "Leorio.say" -> 1; + case "Leorio.listen" -> 0; default -> 0; }; } diff --git a/src/main/java/com/github/nilstrieb/hunterlang/lexer/LexToken.java b/src/main/java/com/github/nilstrieb/hunterlang/lexer/LexToken.java index 76fc9d8..69a35f5 100644 --- a/src/main/java/com/github/nilstrieb/hunterlang/lexer/LexToken.java +++ b/src/main/java/com/github/nilstrieb/hunterlang/lexer/LexToken.java @@ -1,35 +1,52 @@ package com.github.nilstrieb.hunterlang.lexer; +import com.github.nilstrieb.hunterlang.hllibrary.FunctionArgLookup; import com.github.nilstrieb.hunterlang.lib.ConsoleColors; +import com.github.nilstrieb.hunterlang.parser.ParseTreeBodyNode; import com.github.nilstrieb.hunterlang.parser.ParseTreeNode; public class LexToken { WordType key; String value; + private int argOverrideValue = -1; + public LexToken(WordType key, String value) { this.key = key; this.value = value; + + if (key.expectsPostArg() == -1) { + if (key == WordType.LIBFUNCCALL) { + //a function. will only support lib functions + argOverrideValue = FunctionArgLookup.argLookup(value); + } + } } public LexToken(WordType key) { this(key, ""); } - public int expectsPostArgCount() { - return key.expectsPostArg(); + public boolean hasPostfix() { + if(argOverrideValue == -1){ + return key.expectsPostArg() > 0; + } else { + return argOverrideValue > 0; + } + } - public boolean expectsPreArg() { + + public boolean hasPrefix() { return key.expectsPreArg() > 0; } - public ParseTreeNode toNode(){ + public ParseTreeNode toNode() { return new ParseTreeNode(key, value); } @Override public String toString() { - return ConsoleColors.PURPLE_BOLD + "T" + ConsoleColors.BLUE_BOLD + "{Key=" + + return ConsoleColors.PURPLE_BOLD + "T" + ConsoleColors.BLUE_BOLD + "{Key=" + ConsoleColors.RED_BOLD_BRIGHT + key + ConsoleColors.BLUE_BOLD + ", value=" + ConsoleColors.YELLOW + value + ConsoleColors.BLUE_BOLD + "}" + ConsoleColors.RESET; @@ -42,4 +59,12 @@ public class LexToken { public String getValue() { return value; } + + public int postfixCount() { + if(argOverrideValue == -1){ + return key.expectsPostArg(); + } else { + return argOverrideValue; + } + } } diff --git a/src/main/java/com/github/nilstrieb/hunterlang/lexer/Lexer.java b/src/main/java/com/github/nilstrieb/hunterlang/lexer/Lexer.java index 375ae19..fd0739d 100644 --- a/src/main/java/com/github/nilstrieb/hunterlang/lexer/Lexer.java +++ b/src/main/java/com/github/nilstrieb/hunterlang/lexer/Lexer.java @@ -13,7 +13,7 @@ public class Lexer { private static final String MEMCALL = "killua"; public static final String ASSIGNMENT = "hunts"; - public static final String IF = "Gon"; + public static final String IF = "Gon wants"; public static final String WANTS = "wants"; public static final String ELSE = "got"; public static final String BOPEN = "{"; @@ -38,6 +38,7 @@ public class Lexer { private ArrayList temp; public ArrayList lex(String code) { + System.out.println(ConsoleColors.GREEN_BACKGROUND + ConsoleColors.BLACK_BOLD + "------START LEXER------" + ConsoleColors.RESET); tokens = new ArrayList<>(); temp = new ArrayList<>(); @@ -124,8 +125,7 @@ public class Lexer { else if (sub.matches(LIBFUNCCALL_REGEX)) { String libName = sub.replaceAll(LIBFUNCCALL_REGEX, "$1"); String funcName = sub.replaceAll(LIBFUNCCALL_REGEX, "$2"); - addToken(WordType.LIB, libName); - addToken(WordType.FUNCCALL, funcName); + addToken(WordType.LIBFUNCCALL, libName + "." + funcName); j += libName.length(); //lib name j += 1 + 4 + 1; // space + does + space j += funcName.length() - 1; //func name @@ -137,6 +137,7 @@ public class Lexer { temp.clear(); } + System.out.println(ConsoleColors.GREEN_BACKGROUND + ConsoleColors.BLACK_BOLD + "------STOP LEXER------" + ConsoleColors.RESET); return tokens; } @@ -152,11 +153,13 @@ public class Lexer { Lexer l = new Lexer(); String assign = """ + killua70 hunts 3 > 0 killua0 hunts 3 killua0 hunts -3.4 #hunts nothing killua1 hunts "hallo" #comment"""; String ifs = """ + killua0 hunts 3 > 0 Gon wants false { Leorio does say "false" } @@ -165,19 +168,29 @@ public class Lexer { } got { Leorio does say "small killua" } + Gon wants true { + Gon wants true { + 1 + } + } + """; + String sif = """ + Gon wants true { + Leorio does say "false" + """; String hierarchy = """ killua0 hunts 3 > 3 """; - ArrayList tokens = l.lex(hierarchy); + ArrayList tokens = l.lex(sif); Parser p = new Parser(); try { ArrayList nodes = p.parse(tokens); - for (ParseTreeNode node : nodes) { - System.out.println(node); + for (ParseTreeNode parseTreeNode : nodes) { + System.out.println(parseTreeNode); } } catch (ParseException e) { e.printStackTrace(); diff --git a/src/main/java/com/github/nilstrieb/hunterlang/lexer/WordType.java b/src/main/java/com/github/nilstrieb/hunterlang/lexer/WordType.java index 8584f56..6383cd9 100644 --- a/src/main/java/com/github/nilstrieb/hunterlang/lexer/WordType.java +++ b/src/main/java/com/github/nilstrieb/hunterlang/lexer/WordType.java @@ -1,5 +1,7 @@ package com.github.nilstrieb.hunterlang.lexer; +import com.github.nilstrieb.hunterlang.hllibrary.FunctionArgLookup; + public enum WordType { ASSIGNMENT(1, 1), //memory, value MEMCALL(1), //adress @@ -14,15 +16,13 @@ public enum WordType { GTHAN(1, 1), LTHAN(1, 1), EQUALS(1, 1), - FUNCCALL(-1), //args - LIB(1), //function MINUS(1, 1), PLUS(1, 1), MULTIPLY(1, 1), DIVIDE(1, 1), MOD(1, 1), NEGATIVE(1), - EMPTY; + EMPTY, LIBFUNCCALL(-1); private int postArgAmount; private int preArgAmount; diff --git a/src/main/java/com/github/nilstrieb/hunterlang/parser/Parser.java b/src/main/java/com/github/nilstrieb/hunterlang/parser/Parser.java index 0111665..9c6834c 100644 --- a/src/main/java/com/github/nilstrieb/hunterlang/parser/Parser.java +++ b/src/main/java/com/github/nilstrieb/hunterlang/parser/Parser.java @@ -1,116 +1,166 @@ package com.github.nilstrieb.hunterlang.parser; -import com.github.nilstrieb.hunterlang.hllibrary.FunctionArgLookup; import com.github.nilstrieb.hunterlang.lexer.LexToken; import com.github.nilstrieb.hunterlang.lexer.WordType; import com.github.nilstrieb.hunterlang.lib.ConsoleColors; +import com.sun.source.tree.LambdaExpressionTree; import java.util.ArrayList; import java.util.ListIterator; public class Parser { - private ListIterator iterator; - - private LexToken currentToken = null; - private LexToken prevToken = null; - private LexToken nextToken = null; - - private ParseTreeNode prevNode; - public ArrayList parse(ArrayList tokens) throws ParseException { + System.out.println(ConsoleColors.GREEN_BACKGROUND + ConsoleColors.BLACK_BOLD + "------START PARSER------" + ConsoleColors.RESET); - ArrayList statements = new ArrayList<>(); - - iterator = tokens.listIterator(); - - currentToken = iterator.next(); - if (iterator.hasNext()) { - nextToken = iterator.next(); + ArrayList> split = splitStatements(tokens); + for (ArrayList lexTokens : split) { + System.out.println(lexTokens); } - //goes through each statement. On pass -> one statement - do { - System.out.println(ConsoleColors.GREEN_BRIGHT + "NEXT STATEMENT"); + ArrayList list = new ArrayList<>(); - prevNode = new ParseTreeNode(); + ParseTreeNode parent = null; + for (ArrayList statement : split) { + ListIterator iterator = statement.listIterator(); - //start with first token - ParseTreeNode startNode = currentToken.toNode(); + LexToken current = iterator.next(); - //if it wants a pre argument theres an error - if (currentToken.expectsPreArg()) { - throw new ParseException("previous expression expected, doesn't exist"); + switch (current.getKey()) { + case MEMCALL -> { + //assignment + int assignmentIndex = 0; + while (iterator.hasNext()) { + assignmentIndex++; + current = iterator.next(); + if (current.getKey() == WordType.ASSIGNMENT) { + break; + } + } + + parent = current.toNode(); + parent.addChild(evaluate(new ArrayList<>(statement.subList(0, assignmentIndex)))); + parent.addChild(evaluate(new ArrayList<>(statement.subList(assignmentIndex + 1, statement.size())))); + } + case IF, WANTS -> { + parent = new ParseTreeBodyNode(current.toNode()); + ParseTreeBodyNode parentBN = (ParseTreeBodyNode) parent; + while (current.getKey() != WordType.BOPEN) { + current = iterator.next(); + parent.addChild(current.toNode()); + } + + int brackets = 0; + while ((brackets != 0 || current.getKey() != WordType.BCLOSE) && iterator.hasNext()) { + current = iterator.next(); + parentBN.addToken(current); + if (current.getKey() == WordType.BOPEN) { + brackets++; + } else if (current.getKey() == WordType.BCLOSE) { + brackets--; + } + } + parentBN.parse(); + } + case ELSE -> { + + } + case LIBFUNCCALL -> { + + } + case BCLOSE -> { + } + + default -> throw new ParseException("Unexpected value at start of statement: " + current.getKey()); } - //calculate the arguments of the start node, parsing the whole statement - doStatement(startNode, true); - System.out.println(prevNode); - statements.add(prevNode); - } while (iterator.hasNext()); + list.add(parent); + } System.out.println(ConsoleColors.GREEN_BACKGROUND + ConsoleColors.BLACK_BOLD + "------STOP PARSER------" + ConsoleColors.RESET); - return statements; + return list; } - private void doStatement(ParseTreeNode node, boolean isParent) throws ParseException { - if (node.getKey() == WordType.MINUS && prevNode.getKey() != WordType.NUMBER) { - node.setKey(WordType.NEGATIVE); - } + /** + * Evalues an expression. For example something like killuakillua3+4 > 6 + * + * @param tokens The tokens + * @return The parent node + */ + private ParseTreeNode evaluate(ArrayList tokens) throws ParseException { + ListIterator iterator = tokens.listIterator(); + LexToken prev; + LexToken curr; + LexToken next; + ParseTreeNode currentParent; - if (node.expectsPreArg()) { - node.addChild(prevNode, true); - } - int expectedArg = node.expectsPostArgCount(); - if (expectedArg == -1) { - if (node.getKey() == WordType.FUNCCALL) { - //a function. will only support lib functions - System.out.println(currentToken); - expectedArg = FunctionArgLookup.argLookup(prevToken.getValue(), currentToken.getValue()); - } else { - ParseTreeBodyNode bNode = new ParseTreeBodyNode(node); - node = bNode; - fillBody(bNode); - } - } - if (isParent) { - prevNode = node; - } - nextToken(); - if (expectedArg != 0) { - for (int i = 0; i < expectedArg; i++) { - ParseTreeNode child = currentToken.toNode(); - doStatement(child, false); - node.addChild(child); - } - } - - if (currentToken != null && currentToken.expectsPreArg()) { - doStatement(currentToken.toNode(), true); - } - } - - private void fillBody(ParseTreeBodyNode node) throws ParseException { - int level = 0; - nextToken(); - while (currentToken.getKey() != WordType.BCLOSE || level != 0) { - if (currentToken.getKey() == WordType.BOPEN) { - level++; - } else if (currentToken.getKey() == WordType.BCLOSE) { - level--; - } - node.addToken(currentToken); - nextToken(); - } - node.parse(); - } - - private void nextToken() { - prevToken = currentToken; - currentToken = nextToken; + prev = iterator.next(); if (iterator.hasNext()) { - nextToken = iterator.next(); + curr = iterator.next(); + } else { + //1 token + return prev.toNode(); } + + if (iterator.hasNext()) { + next = iterator.next(); + } else { + //2 tokens + currentParent = prev.toNode(); + currentParent.addChild(curr.toNode()); + return currentParent; + } + + if (curr.hasPrefix()) { + currentParent = curr.toNode(); + currentParent.addChild(prev.toNode(), true); + currentParent.addChild(evaluate(new ArrayList<>(tokens.subList(2, tokens.size())))); + } else if (prev.postfixCount() == 1) { + currentParent = prev.toNode(); + currentParent.addChild(evaluate(new ArrayList<>(tokens.subList(1, tokens.size())))); + } else { + throw new ParseException("more than one arg: " + prev.postfixCount() + " thrown on: " + prev + " all: " + tokens); + } + return currentParent; + } + + private ArrayList> splitStatements(ArrayList tokens) throws ParseException { + System.out.println(ConsoleColors.YELLOW_BACKGROUND_BRIGHT + ConsoleColors.BLACK_BOLD + "SPLITTER" + ConsoleColors.RESET); + ArrayList> tokensSplit = new ArrayList<>(); + tokensSplit.add(new ArrayList<>()); + + ListIterator iterator = tokens.listIterator(); + + if (tokens.size() < 2) { + throw new ParseException("code has to consist of at least 2 tokens"); + } + + LexToken prev; + LexToken curr = iterator.next(); + tokensSplit.get(0).add(curr); + + int i = 0; + int brackets = 0; + while (iterator.hasNext()) { + prev = curr; + curr = iterator.next(); + + if (curr.getKey() == WordType.BOPEN) { + brackets++; + } + if (brackets == 0 && !prev.hasPostfix() && !curr.hasPrefix()) { + //when there is no connection between two tokens, they are part of seperate statements + tokensSplit.add(new ArrayList<>()); + i++; + } + + tokensSplit.get(i).add(curr); + + if (curr.getKey() == WordType.BCLOSE) { + brackets--; + } + } + return tokensSplit; } } \ No newline at end of file