From 23eb0166657bfcc36c8f2114dec80641c5d2d284 Mon Sep 17 00:00:00 2001 From: Nathanael Sensfelder Date: Fri, 3 Jul 2020 11:22:06 +0200 Subject: ... --- src/core/Makefile | 6 +- .../fate/v1/error/ContextCycleException.java | 47 ++++++++ .../src/tonkadur/fate/v1/error/InputException.java | 40 +++---- .../v1/error/TypeAlreadyDeclaredException.java | 10 +- src/core/src/tonkadur/fate/v1/parser/Context.java | 126 +++++++++++++++++++++ src/core/src/tonkadur/fate/v1/parser/FateLexer.g4 | 5 +- src/core/src/tonkadur/fate/v1/parser/FateParser.g4 | 64 +++++++++-- src/core/src/tonkadur/fate/v1/parser/Location.java | 114 +++++++++++++++++++ src/core/src/tonkadur/fate/v1/parser/Origin.java | 33 ++++++ 9 files changed, 401 insertions(+), 44 deletions(-) create mode 100644 src/core/src/tonkadur/fate/v1/error/ContextCycleException.java create mode 100644 src/core/src/tonkadur/fate/v1/parser/Context.java create mode 100644 src/core/src/tonkadur/fate/v1/parser/Location.java create mode 100644 src/core/src/tonkadur/fate/v1/parser/Origin.java diff --git a/src/core/Makefile b/src/core/Makefile index 178f01b..ffca63b 100644 --- a/src/core/Makefile +++ b/src/core/Makefile @@ -80,11 +80,9 @@ clean: rm -rf $(BIN_DIR)/* rm -rf $(TARGET) $(STANDALONE) -$(SRC_DIR)/tonkadur/parser/LangParser.java: $(ANTLR_SOURCES) - # Pattern rules can be used to generate multiple target in a single action. -LangLexer%java LangParser%java: $(ANTLR_SOURCES) - $(JAVA) -jar $(ANTLR_JAR) -lib $(SRC_DIR)/tonkadur/parser/ $^ +%Lexer.java %Parser.java: $(ANTLR_SOURCES) + $(JAVA) -jar $(ANTLR_JAR) -lib $(dir $@) $^ $(CLASSES): $(BIN_DIR)/%.class: $(SRC_DIR)/%.java $(BIN_DIR) $(JAVAC) -cp $(CLASSPATH) -d $(BIN_DIR) $< diff --git a/src/core/src/tonkadur/fate/v1/error/ContextCycleException.java b/src/core/src/tonkadur/fate/v1/error/ContextCycleException.java new file mode 100644 index 0000000..c5a2434 --- /dev/null +++ b/src/core/src/tonkadur/fate/v1/error/ContextCycleException.java @@ -0,0 +1,47 @@ +package tonkadur.fate.v1.error; + +import tonkadur.fate.v1.parser.Location; +import tonkadur.fate.v1.parser.Origin; + +public class ContextCycleException extends InputException +{ + /***************************************************************************/ + /**** MEMBERS **************************************************************/ + /***************************************************************************/ + /* + * Using a Location instead of an Origin here, because the file refers to + * something in 'origin' anyway. + */ + protected final Location original_require_location; + protected final String filename; + + /***************************************************************************/ + /**** PUBLIC ***************************************************************/ + /***************************************************************************/ + public ContextCycleException + ( + final Location original_require_location, + final String filename + ) + { + this.original_require_location = original_require_location; + this.filename = filename; + } + + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder(); + + sb.append(origin.get_context().toString()); + sb.append("Cyclic dependency for file '"); + sb.append(filename); + sb.append("' required at "); + sb.append(origin.get_location().toString()); + sb.append(" when it was already required at "); + sb.append(original_require_location.toString()); + sb.append("."); + + return sb.toString(); + } +} diff --git a/src/core/src/tonkadur/fate/v1/error/InputException.java b/src/core/src/tonkadur/fate/v1/error/InputException.java index d2921cb..71b1878 100644 --- a/src/core/src/tonkadur/fate/v1/error/InputException.java +++ b/src/core/src/tonkadur/fate/v1/error/InputException.java @@ -1,5 +1,8 @@ package tonkadur.fate.v1.error; +import tonkadur.fate.v1.parser.Context; +import tonkadur.fate.v1.parser.Origin; + abstract class InputException extends Throwable { /***************************************************************************/ @@ -10,43 +13,28 @@ abstract class InputException extends Throwable /***************************************************************************/ /**** MEMBERS **************************************************************/ /***************************************************************************/ - protected String filename = ""; - protected int line = -1; - protected int column = -1; + protected Origin origin; /***************************************************************************/ /**** PUBLIC ***************************************************************/ /***************************************************************************/ - public InputException set_location + public void set_origin (final Origin origin) + { + this.origin = origin; + } + + public void set_origin ( - final String filename, + final Context context, final int line, final int column ) { - this.filename = filename; - this.line = line; - this.column = column; - - return this; - } - - public String get_location () - { - final StringBuilder sb = new StringBuilder(); - - sb.append(filename); - sb.append(":"); - sb.append(line); - sb.append(","); - sb.append(column); - - return sb.toString(); + origin = context.get_origin_at(line, column); } - @Override - public String toString () + public Origin get_origin () { - return get_location(); + return origin; } } diff --git a/src/core/src/tonkadur/fate/v1/error/TypeAlreadyDeclaredException.java b/src/core/src/tonkadur/fate/v1/error/TypeAlreadyDeclaredException.java index 673a3bd..b028127 100644 --- a/src/core/src/tonkadur/fate/v1/error/TypeAlreadyDeclaredException.java +++ b/src/core/src/tonkadur/fate/v1/error/TypeAlreadyDeclaredException.java @@ -22,11 +22,13 @@ public class TypeAlreadyDeclaredException extends InputException { final StringBuilder sb = new StringBuilder(); - sb.append(super.toString()); - sb.append(" Type '"); + sb.append(origin.get_context().toString()); + sb.append("Declaration for type '"); sb.append(original_type.get_name()); - sb.append("' already declared in "); - sb.append(original_type.get_source().toString()); + sb.append("' at "); + sb.append(origin.get_location().toString()); + sb.append(" when it was already declared at "); + sb.append(original_type.get_origin().get_location().toString()); sb.append("."); return sb.toString(); diff --git a/src/core/src/tonkadur/fate/v1/parser/Context.java b/src/core/src/tonkadur/fate/v1/parser/Context.java new file mode 100644 index 0000000..3ad958d --- /dev/null +++ b/src/core/src/tonkadur/fate/v1/parser/Context.java @@ -0,0 +1,126 @@ +package tonkadur.fate.v1.parser; + +import java.util.Stack; + +import tonkadur.fate.v1.error.ContextCycleException; + +public class Context +{ + /***************************************************************************/ + /**** MEMBERS **************************************************************/ + /***************************************************************************/ + protected final boolean locked; + protected final Stack source; + protected String current_file; + + /***************************************************************************/ + /**** PUBLIC ***************************************************************/ + /***************************************************************************/ + + /**** Constructors *********************************************************/ + public Context (final String filename) + { + locked = false; + source = new Stack(); + current_file = filename; + } + + /**** Accessors ************************************************************/ + public void push (final Location location, final String new_file) + throws ContextCycleException + { + throw_exception_on_cycle(new_file); + + current_file = new_file; + + source.push(location); + } + + public void pop () + { + current_file = source.peek().get_filename(); + source.pop(); + } + + /**** Utils ****************************************************************/ + public Origin get_origin_at (final int line, final int column) + { + return new Origin(this, new Location(current_file, line, column)); + } + + /**** Misc. ****************************************************************/ + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder(); + + /* + * That's in FIFO order, as we want it to be, due to arguable design + * decisions in Java. + */ + for (final Location location: source) + { + sb.append("Require at "); + sb.append(location.toString()); + sb.append(" led to"); + sb.append(System.lineSeparator()); + } + + return sb.toString(); + } + + @Override + public boolean equals (final Object o) + { + if (o instanceof Context) + { + final Context b; + + b = (Context) o; + + return + ( + (source.equals(b.source)) + && (current_file.equals (b.current_file)) + ); + } + + return false; + } + + @Override + public int hashCode () + { + return (source.hashCode() + current_file.hashCode()); + } + + /***************************************************************************/ + /**** PROTECTED ************************************************************/ + /***************************************************************************/ + protected void throw_exception_on_cycle (final String new_file) + throws ContextCycleException + { + Location previous_import; + + previous_import = null; + + /* + * That's in FIFO order, as we want it to be, due to arguable design + * decisions in Java. + */ + for (final Location location: source) + { + if (location.get_filename().equals(new_file)) + { + throw new ContextCycleException(previous_import, new_file); + } + + previous_import = location; + } + + if (current_file.equals(new_file)) + { + throw new ContextCycleException(previous_import, new_file); + } + } +} diff --git a/src/core/src/tonkadur/fate/v1/parser/FateLexer.g4 b/src/core/src/tonkadur/fate/v1/parser/FateLexer.g4 index 80fe1fd..3671a4f 100644 --- a/src/core/src/tonkadur/fate/v1/parser/FateLexer.g4 +++ b/src/core/src/tonkadur/fate/v1/parser/FateLexer.g4 @@ -5,8 +5,6 @@ lexer grammar FateLexer; package tonkadur.fate.v1.parser; } - - fragment SEP: [ \t\r\n]+; WS: SEP; @@ -53,6 +51,9 @@ PARAMETER_KW: L_PAREN 'parameter' WS*; PLUS_KW: L_PAREN 'plus' WS*; POWER_KW: L_PAREN 'power' WS*; RANDOM_KW: L_PAREN 'random' WS*; +DECLARE_TEXT_EFFECT_KW: L_PAREN 'declare_text_effect' WS*; +PLAYER_CHOICE_KW: L_PAREN 'player_choice' WS*; +CAST_KW: L_PAREN 'cast' WS*; REMOVE_ALL_KW: L_PAREN 'remove_all' WS*; REMOVE_ONE_KW: L_PAREN 'remove_one' WS*; REQUIRE_KW: L_PAREN 'require' WS*; diff --git a/src/core/src/tonkadur/fate/v1/parser/FateParser.g4 b/src/core/src/tonkadur/fate/v1/parser/FateParser.g4 index c0be842..408d71d 100644 --- a/src/core/src/tonkadur/fate/v1/parser/FateParser.g4 +++ b/src/core/src/tonkadur/fate/v1/parser/FateParser.g4 @@ -123,25 +123,67 @@ general_fate_instr: { } - | NEWLINE_KW + | IF_ELSE_KW value WS+ general_fate_instr WS+ general_fate_instr R_PAREN + { + } + + | COND_KW instr_cond_list R_PAREN + { + } + + | PLAYER_CHOICE_KW player_choice* R_PAREN + { + } + + | text+ + { + } +; + +instr_cond_list: + (L_PAREN value WS+ general_fate_instr R_PAREN)+ + { + } +; + +player_choice: + L_PAREN L_PAREN text+ R_PAREN WS+ general_fate_instr R_PAREN + { + } + + | IF_KW value WS+ player_choice R_PAREN + { + } + + | IF_ELSE_KW value WS+ player_choice WS+ player_choice R_PAREN { } - | text + | COND_KW player_choice_cond_list R_PAREN + { + } +; + +player_choice_cond_list: + (L_PAREN value WS+ player_choice R_PAREN)+ { } ; text: - sentence text* + sentence { } - | (ENABLE_TEXT_PARAMETER_KW WORD WS+ text R_PAREN) text* + | ENABLE_TEXT_PARAMETER_KW WORD WS+ text+ R_PAREN { } - | non_text_value text* + | NEWLINE_KW + { + } + + | non_text_value { } ; @@ -289,7 +331,7 @@ value: ; non_text_value: - | IF_ELSE_KW value WS+ value WS+ value R_PAREN + IF_ELSE_KW value WS+ value WS+ value R_PAREN { } @@ -305,6 +347,10 @@ non_text_value: { } + | CAST_KW WORD WORD value R_PAREN + { + } + | value_reference { } @@ -325,13 +371,15 @@ value_reference: ; value_cond_list: - (L_PAREN value WS value R_PAREN)+ + (L_PAREN value WS+ value R_PAREN)+ { } ; value_list: - value* + value* (WS+ value)* { } ; + + diff --git a/src/core/src/tonkadur/fate/v1/parser/Location.java b/src/core/src/tonkadur/fate/v1/parser/Location.java new file mode 100644 index 0000000..98f407d --- /dev/null +++ b/src/core/src/tonkadur/fate/v1/parser/Location.java @@ -0,0 +1,114 @@ +package tonkadur.fate.v1.parser; + +public class Location +{ + /***************************************************************************/ + /**** STATIC MEMBERS *******************************************************/ + /***************************************************************************/ + public static final Location BASE_LANGUAGE; + + static + { + BASE_LANGUAGE = new Location(true, "", -1, -1); + } + + /***************************************************************************/ + /**** MEMBERS **************************************************************/ + /***************************************************************************/ + protected final boolean is_base_language; + protected final String filename; + protected final int line; + protected final int column; + + /***************************************************************************/ + /**** PROTECTED ************************************************************/ + /***************************************************************************/ + protected Location + ( + final boolean is_base_language, + final String filename, + final int line, + final int column + ) + { + this.is_base_language = is_base_language; + this.filename = filename; + this.line = line; + this.column = column; + } + + /***************************************************************************/ + /**** PUBLIC ***************************************************************/ + /***************************************************************************/ + + /**** Constructors *********************************************************/ + public Location + ( + final String filename, + final int line, + final int column + ) + { + this.is_base_language = false; + this.filename = filename; + this.line = line; + this.column = column; + } + + /**** Accessors ************************************************************/ + public String get_filename () + { + return filename; + } + + public int get_line () + { + return line; + } + + public int get_column () + { + return line; + } + + /**** Misc. ****************************************************************/ + @Override + public String toString () + { + final StringBuilder sb = new StringBuilder(); + + sb.append(filename); + sb.append(":"); + sb.append(line); + sb.append(","); + sb.append(column); + + return sb.toString(); + } + + @Override + public boolean equals (final Object o) + { + if (o instanceof Location) + { + final Location b; + + b = (Location) o; + + return + ( + (filename.equals(b.filename)) + && (line == b.line) + && (column == b.column) + ); + } + + return false; + } + + @Override + public int hashCode () + { + return (filename.hashCode() + line + column); + } +} diff --git a/src/core/src/tonkadur/fate/v1/parser/Origin.java b/src/core/src/tonkadur/fate/v1/parser/Origin.java new file mode 100644 index 0000000..cd4b784 --- /dev/null +++ b/src/core/src/tonkadur/fate/v1/parser/Origin.java @@ -0,0 +1,33 @@ +package tonkadur.fate.v1.parser; + +public class Origin +{ + /***************************************************************************/ + /**** MEMBERS **************************************************************/ + /***************************************************************************/ + protected final Context context; + protected final Location location; + + /***************************************************************************/ + /**** PUBLIC ***************************************************************/ + /***************************************************************************/ + public Origin + ( + final Context context, + final Location location + ) + { + this.context = context; + this.location = location; + } + + public Context get_context () + { + return context; + } + + public Location get_location () + { + return location; + } +} -- cgit v1.2.3-70-g09d2