From 313a58463126e2ad54ba829f8610a0988b08c078 Mon Sep 17 00:00:00 2001 From: Nilstrieb Date: Sat, 16 Jan 2021 18:11:18 +0100 Subject: [PATCH] assignment parsing works --- .idea/.gitignore | 2 +- .../hunterlang/editor/EditorView.form | 21 ---- .../hunterlang/editor/EditorView.java | 8 -- .../hllibrary/FunctionArgLookup.java | 15 +++ .../nilstrieb/hunterlang/lexer/LexToken.java | 28 ++++- .../nilstrieb/hunterlang/lexer/LexedList.java | 27 ---- .../nilstrieb/hunterlang/lexer/Lexer.java | 118 +++++++++++------- .../nilstrieb/hunterlang/lexer/WordType.java | 46 ++++++- .../hunterlang/parser/ParseException.java | 7 ++ .../hunterlang/parser/ParseTreeBodyNode.java | 44 +++++++ .../hunterlang/parser/ParseTreeNode.java | 99 +++++++++++++++ .../nilstrieb/hunterlang/parser/Parser.java | 117 ++++++++++++++++- 12 files changed, 424 insertions(+), 108 deletions(-) delete mode 100644 src/main/java/com/github/nilstrieb/hunterlang/editor/EditorView.form delete mode 100644 src/main/java/com/github/nilstrieb/hunterlang/editor/EditorView.java create mode 100644 src/main/java/com/github/nilstrieb/hunterlang/hllibrary/FunctionArgLookup.java delete mode 100644 src/main/java/com/github/nilstrieb/hunterlang/lexer/LexedList.java create mode 100644 src/main/java/com/github/nilstrieb/hunterlang/parser/ParseException.java create mode 100644 src/main/java/com/github/nilstrieb/hunterlang/parser/ParseTreeBodyNode.java create mode 100644 src/main/java/com/github/nilstrieb/hunterlang/parser/ParseTreeNode.java diff --git a/.idea/.gitignore b/.idea/.gitignore index 69ee9e0..a203e0d 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -2,4 +2,4 @@ /shelf/ /workspace.xml out\ -.class \ No newline at end of file +*.class \ No newline at end of file diff --git a/src/main/java/com/github/nilstrieb/hunterlang/editor/EditorView.form b/src/main/java/com/github/nilstrieb/hunterlang/editor/EditorView.form deleted file mode 100644 index 43d331a..0000000 --- a/src/main/java/com/github/nilstrieb/hunterlang/editor/EditorView.form +++ /dev/null @@ -1,21 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - -
diff --git a/src/main/java/com/github/nilstrieb/hunterlang/editor/EditorView.java b/src/main/java/com/github/nilstrieb/hunterlang/editor/EditorView.java deleted file mode 100644 index 6b996a2..0000000 --- a/src/main/java/com/github/nilstrieb/hunterlang/editor/EditorView.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.nilstrieb.hunterlang.editor; - -import javax.swing.*; - -public class EditorView { - private JEditorPane editorPane1; - private JPanel panel1; -} diff --git a/src/main/java/com/github/nilstrieb/hunterlang/hllibrary/FunctionArgLookup.java b/src/main/java/com/github/nilstrieb/hunterlang/hllibrary/FunctionArgLookup.java new file mode 100644 index 0000000..870ab37 --- /dev/null +++ b/src/main/java/com/github/nilstrieb/hunterlang/hllibrary/FunctionArgLookup.java @@ -0,0 +1,15 @@ +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; + }; + 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 3fa8c45..76fc9d8 100644 --- a/src/main/java/com/github/nilstrieb/hunterlang/lexer/LexToken.java +++ b/src/main/java/com/github/nilstrieb/hunterlang/lexer/LexToken.java @@ -1,6 +1,7 @@ package com.github.nilstrieb.hunterlang.lexer; import com.github.nilstrieb.hunterlang.lib.ConsoleColors; +import com.github.nilstrieb.hunterlang.parser.ParseTreeNode; public class LexToken { WordType key; @@ -11,11 +12,34 @@ public class LexToken { this.value = value; } + public LexToken(WordType key) { + this(key, ""); + } + + public int expectsPostArgCount() { + return key.expectsPostArg(); + } + public boolean expectsPreArg() { + return key.expectsPreArg() > 0; + } + + public ParseTreeNode toNode(){ + return new ParseTreeNode(key, value); + } + @Override public String toString() { - return 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.YELLOW + value + ConsoleColors.BLUE_BOLD + "}" + ConsoleColors.RESET; + } + + public WordType getKey() { + return key; + } + + public String getValue() { + return value; } } diff --git a/src/main/java/com/github/nilstrieb/hunterlang/lexer/LexedList.java b/src/main/java/com/github/nilstrieb/hunterlang/lexer/LexedList.java deleted file mode 100644 index fe37092..0000000 --- a/src/main/java/com/github/nilstrieb/hunterlang/lexer/LexedList.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.nilstrieb.hunterlang.lexer; - -import java.util.ArrayList; - -public class LexedList { - private ArrayList> list; - - public LexedList() { - list = new ArrayList<>(); - } - - public void newLine(){ - list.add(new ArrayList<>()); - } - - public void add(int line, WordType type, String value) { - list.get(line).add(new LexToken(type, value)); - } - - public void add(int line, WordType type){ - add(line, type, ""); - } - - public ArrayList get(int i) { - return list.get(i); - } -} 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 82b0339..b58a87f 100644 --- a/src/main/java/com/github/nilstrieb/hunterlang/lexer/Lexer.java +++ b/src/main/java/com/github/nilstrieb/hunterlang/lexer/Lexer.java @@ -1,6 +1,9 @@ package com.github.nilstrieb.hunterlang.lexer; import com.github.nilstrieb.hunterlang.lib.ConsoleColors; +import com.github.nilstrieb.hunterlang.parser.ParseException; +import com.github.nilstrieb.hunterlang.parser.ParseTreeNode; +import com.github.nilstrieb.hunterlang.parser.Parser; import java.util.ArrayList; @@ -31,15 +34,16 @@ public class Lexer { public static final String STRING_REGEX = "^\"(.*)\".*"; public static final String NUMBER_REGEX = "^(-?\\d+.?\\d*).*"; - private LexedList tokens; + private ArrayList tokens; + private ArrayList temp; + + public ArrayList lex(String code) { + tokens = new ArrayList<>(); + temp = new ArrayList<>(); - public LexedList lex(String code) { - tokens = new LexedList(); - ArrayList tempList = new ArrayList<>(); String[] lines = code.split("\\r\\n|\\n"); for (int i = 0; i < lines.length; i++) { - tokens.newLine(); String line = lines[i]; for (int j = 0; j < line.length(); j++) { String sub = line.substring(j); @@ -53,103 +57,127 @@ public class Lexer { //FIXED KEYWORDS else if (sub.startsWith(MEMCALL)) { - tokens.add(i, WordType.MEMCALL); + addToken(WordType.MEMCALL); j += MEMCALL.length() - 1; } else if (sub.startsWith(ASSIGNMENT)) { - tokens.add(i, WordType.ASSIGNMENT); + addToken(WordType.ASSIGNMENT); j += ASSIGNMENT.length() - 1; } else if (sub.startsWith(IF)) { - tokens.add(i, WordType.IF); + addToken(WordType.IF); j += IF.length() - 1; - } else if (sub.startsWith(WANTS)){ - tokens.add(i, WordType.WANTS); + } else if (sub.startsWith(WANTS)) { + addToken(WordType.WANTS); j += WANTS.length() - 1; - } else if (sub.startsWith(ELSE)){ - tokens.add(i, WordType.ELSE); + } else if (sub.startsWith(ELSE)) { + addToken(WordType.ELSE); j += ELSE.length() - 1; } //CONDITIONS - else if (sub.startsWith(GTHAN)){ - tokens.add(i, WordType.GTHAN); + else if (sub.startsWith(GTHAN)) { + addToken(WordType.GTHAN); j += GTHAN.length() - 1; - } else if (sub.startsWith(LTHAN)){ - tokens.add(i, WordType.LTHAN); + } else if (sub.startsWith(LTHAN)) { + addToken(WordType.LTHAN); j += LTHAN.length() - 1; - } else if (sub.startsWith(EQUALS)){ - tokens.add(i, WordType.EQUALS); + } else if (sub.startsWith(EQUALS)) { + addToken(WordType.EQUALS); j += EQUALS.length() - 1; } //OPERATORS - else if (sub.startsWith(PLUS)){ - tokens.add(i, WordType.PLUS); - } else if (sub.startsWith(MINUS)){ - tokens.add(i, WordType.MINUS); - } else if (sub.startsWith(MULTIPLY)){ - tokens.add(i, WordType.MULTIPLY); - }else if (sub.startsWith(DIVIDE)){ - tokens.add(i, WordType.DIVIDE); - } else if (sub.startsWith(MOD)){ - tokens.add(i, WordType.MOD); + else if (sub.startsWith(PLUS)) { + addToken(WordType.PLUS); + } else if (sub.startsWith(MINUS)) { + addToken(WordType.MINUS); + } else if (sub.startsWith(MULTIPLY)) { + addToken(WordType.MULTIPLY); + } else if (sub.startsWith(DIVIDE)) { + addToken(WordType.DIVIDE); + } else if (sub.startsWith(MOD)) { + addToken(WordType.MOD); } // BRACKETS - else if (sub.startsWith(BOPEN)){ - tokens.add(i, WordType.BOPEN); - } else if (sub.startsWith(BCLOSE)){ - tokens.add(i, WordType.BCLOSE); + else if (sub.startsWith(BOPEN)) { + addToken(WordType.BOPEN); + } else if (sub.startsWith(BCLOSE)) { + addToken(WordType.BCLOSE); } //VALUES else if (sub.matches(STRING_REGEX)) { String string = sub.replaceAll(STRING_REGEX, "$1"); - tokens.add(i, WordType.STRING, string); + addToken(WordType.STRING, string); j += string.length() + 1; } else if (sub.matches(NUMBER_REGEX)) { String number = sub.replaceAll(NUMBER_REGEX, "$1"); - tokens.add(i, WordType.NUMBER, number); + addToken(WordType.NUMBER, number); j += number.length() - 1; - } else if (sub.matches(BOOL_REGEX)){ + } else if (sub.matches(BOOL_REGEX)) { String bool = sub.replaceAll(BOOL_REGEX, "$1"); - tokens.add(i, WordType.BOOL, bool); + addToken(WordType.BOOL, bool); j += bool.length() - 1; } //Calls - else if(sub.matches(LIBFUNCCALL_REGEX)){ + else if (sub.matches(LIBFUNCCALL_REGEX)) { String libName = sub.replaceAll(LIBFUNCCALL_REGEX, "$1"); String funcName = sub.replaceAll(LIBFUNCCALL_REGEX, "$2"); - tokens.add(i, WordType.LIB, libName); - tokens.add(i, WordType.FUNCCALL, funcName); + addToken(WordType.LIB, libName); + addToken(WordType.FUNCCALL, funcName); j += libName.length(); //lib name j += 1 + 4 + 1; // space + does + space j += funcName.length() - 1; //func name } } System.out.println(ConsoleColors.GREEN_BOLD + line); - System.out.println(ConsoleColors.BLUE_BOLD + tokens.get(i)); + System.out.println(ConsoleColors.BLUE_BOLD + temp); + tokens.addAll(temp); + temp.clear(); } return tokens; } + private void addToken(WordType type, String value) { + temp.add(new LexToken(type, value)); + } + + private void addToken(WordType type) { + temp.add(new LexToken(type)); + } + public static void main(String[] args) { Lexer l = new Lexer(); - LexedList tokens = l.lex(""" + String assign = """ killua0 hunts 3 killua0 hunts -3.4 #hunts nothing - killua0 hunts 4 killua1 hunts "hallo" - #comment + #comment"""; + String ifs = """ Gon wants false { Leorio does say "false" - } wants killua0 > 3 { + } + wants killua0 > 3 { Leorio does say "big killua" } got { Leorio does say "small killua" } - """); + """; + + ArrayList tokens = l.lex(ifs); + + Parser p = new Parser(); + try { + ArrayList nodes = p.parse(tokens); + + for (ParseTreeNode node : nodes) { + System.out.println(node); + } + } 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 2bf17c0..c26609f 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,49 @@ package com.github.nilstrieb.hunterlang.lexer; public enum WordType { - ASSIGNMENT, MEMCALL, STRING, NUMBER, BOOL, IF, WANTS, ELSE, BOPEN, BCLOSE, GTHAN, LTHAN, EQUALS, FUNCNAME, FUNCARGS, FUNCRETURN, IOCALL, FUNCCALL, OPERATOR, LIB, MINUS, DEFAULT, PLUS, MULTIPLY, DIVIDE, MOD; + ASSIGNMENT(1, 1), //memory, value + MEMCALL(1), //adress + STRING, + NUMBER, + BOOL, + IF(1), //wants + WANTS(2), //condition, body + ELSE(1), //body + BOPEN(-1), //body, close + BCLOSE, + 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; + + private int postArgAmount; + private int preArgAmount; + + WordType(int postArgAmount, int preArgAmount) { + this.postArgAmount = postArgAmount; + this.preArgAmount = preArgAmount; + } + + WordType(int postArgAmount) { + this(postArgAmount, 0); + } + + WordType() { + this(0, 0); + } + + public int expectsPostArg() { + return postArgAmount; + } + public int expectsPreArg() { + return preArgAmount; + } } diff --git a/src/main/java/com/github/nilstrieb/hunterlang/parser/ParseException.java b/src/main/java/com/github/nilstrieb/hunterlang/parser/ParseException.java new file mode 100644 index 0000000..15f38c2 --- /dev/null +++ b/src/main/java/com/github/nilstrieb/hunterlang/parser/ParseException.java @@ -0,0 +1,7 @@ +package com.github.nilstrieb.hunterlang.parser; + +public class ParseException extends Exception{ + public ParseException(String message) { + super(message); + } +} diff --git a/src/main/java/com/github/nilstrieb/hunterlang/parser/ParseTreeBodyNode.java b/src/main/java/com/github/nilstrieb/hunterlang/parser/ParseTreeBodyNode.java new file mode 100644 index 0000000..ee70b1b --- /dev/null +++ b/src/main/java/com/github/nilstrieb/hunterlang/parser/ParseTreeBodyNode.java @@ -0,0 +1,44 @@ +package com.github.nilstrieb.hunterlang.parser; + +import com.github.nilstrieb.hunterlang.lexer.LexToken; +import com.github.nilstrieb.hunterlang.lexer.WordType; + +import java.util.ArrayList; + +public class ParseTreeBodyNode extends ParseTreeNode{ + private ArrayList statements; + private ArrayList tokens; + + public ParseTreeBodyNode(WordType key, String value) { + super(key, value); + this.statements = new ArrayList<>(); + this.tokens = new ArrayList<>(); + } + + public ParseTreeBodyNode(ParseTreeNode node) { + this(node.key, node.value); + } + + public ArrayList getStatements() { + return statements; + } + + public void addToken(LexToken currentToken) { + tokens.add(currentToken); + } + + @Override + public String toString() { + return super.toString() + statements; + } + + @Override + public String toString(String indent) { + return super.toString(indent) + statements; + } + + public void parse() throws ParseException { + Parser p = new Parser(); + statements = p.parse(tokens); + } +} diff --git a/src/main/java/com/github/nilstrieb/hunterlang/parser/ParseTreeNode.java b/src/main/java/com/github/nilstrieb/hunterlang/parser/ParseTreeNode.java new file mode 100644 index 0000000..16b0fc7 --- /dev/null +++ b/src/main/java/com/github/nilstrieb/hunterlang/parser/ParseTreeNode.java @@ -0,0 +1,99 @@ +package com.github.nilstrieb.hunterlang.parser; + +import com.github.nilstrieb.hunterlang.lexer.WordType; +import com.github.nilstrieb.hunterlang.lib.ConsoleColors; + +import java.util.ArrayList; + +public class ParseTreeNode { + WordType key; + String value; + ArrayList children; + + int remainingPreArgs; + int remainingPostArgs; + + public ParseTreeNode(WordType key, String value) { + this.key = key; + this.value = value; + children = new ArrayList<>(); + remainingPostArgs = key.expectsPostArg(); + remainingPreArgs = key.expectsPreArg(); + } + + public ParseTreeNode() { + this(WordType.EMPTY, ""); + } + + public ArrayList getChildren() { + return children; + } + + public void addChild(ParseTreeNode n, boolean pre) { + children.add(n); + if (pre) { + remainingPreArgs--; + } + } + + public void addChild(ParseTreeNode n) { + addChild(n, false); + } + + public int expectsPostArgCount() { + return remainingPostArgs; + } + + public boolean expectsPreArg() { + return remainingPreArgs > 0; + } + + public void setKey(WordType key) { + this.key = key; + remainingPostArgs = key.expectsPostArg(); + remainingPreArgs = key.expectsPreArg(); + } + + @Override + public String toString() { + return ConsoleColors.PURPLE_BOLD + "N" + ConsoleColors.BLUE_BOLD + "{name=" + + ConsoleColors.RED_BOLD_BRIGHT + key + + ConsoleColors.BLUE_BOLD + ", value=" + + ConsoleColors.YELLOW + value + + ConsoleColors.BLUE_BOLD + ", children=" + printList(children, "") + + ConsoleColors.BLUE_BOLD + "}" + ConsoleColors.RESET; + } + + public String toString(String indent) { + return ConsoleColors.PURPLE_BOLD + indent + "N" + ConsoleColors.BLUE_BOLD + "{name=" + + ConsoleColors.RED_BOLD_BRIGHT + key + + ConsoleColors.BLUE_BOLD + ", value=" + + ConsoleColors.YELLOW + value + + ConsoleColors.BLUE_BOLD + ", children=" + printList(children, indent + " ") + + ConsoleColors.BLUE_BOLD + "}" + ConsoleColors.RESET; + } + + public String printList(ArrayList list, String indent) { + if(list.size() == 0){ + return "[]"; + } + + String indentHigher = indent + " "; + + StringBuilder sb = new StringBuilder(); + for (ParseTreeNode node : list) { + sb.append("[\n"); + sb.append(node.toString(indentHigher)); + } + sb.append("]"); + return sb.toString(); + } + + public WordType getKey() { + return key; + } + + public String getValue() { + return value; + } +} 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 83017e7..2a10b29 100644 --- a/src/main/java/com/github/nilstrieb/hunterlang/parser/Parser.java +++ b/src/main/java/com/github/nilstrieb/hunterlang/parser/Parser.java @@ -1,7 +1,118 @@ package com.github.nilstrieb.hunterlang.parser; -public class 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; -class ParseTreeNode { +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(); + } + + //goes through each statement. On pass -> one statement + do { + System.out.println(ConsoleColors.GREEN_BRIGHT + "NEXT STATEMENT"); + + prevNode = new ParseTreeNode(); + + //start with first token + ParseTreeNode startNode = currentToken.toNode(); + + //if it wants a pre argument theres an error + if (currentToken.expectsPreArg()) { + throw new ParseException("previous expression expected, doesn't exist"); + } + + //calculate the arguments of the start node, parsing the whole statement + getArguments(startNode, true); + System.out.println(prevNode); + statements.add(prevNode); + } while (iterator.hasNext()); + + System.out.println(ConsoleColors.GREEN_BACKGROUND + ConsoleColors.BLACK_BOLD + "------STOP PARSER------" + ConsoleColors.RESET); + return statements; + } + + private void getArguments(ParseTreeNode node, boolean isParent) throws ParseException { + if (node.getKey() == WordType.MINUS && prevNode.getKey() != WordType.NUMBER) { + node.setKey(WordType.NEGATIVE); + } + + 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; + } + if (expectedArg == 0) { + nextToken(); + } else { + nextToken(); + for (int i = 0; i < expectedArg; i++) { + ParseTreeNode child = currentToken.toNode(); + getArguments(child, false); + node.addChild(child); + } + } + + if (currentToken != null && currentToken.expectsPreArg()) { + getArguments(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; + if (iterator.hasNext()) { + nextToken = iterator.next(); + } + } } \ No newline at end of file