summaryrefslogtreecommitdiff
path: root/addons/escoria-core/game/core-scripts/esc/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'addons/escoria-core/game/core-scripts/esc/compiler')
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd75
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd2
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd4
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd4
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd62
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd249
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd64
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd2
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd205
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd12
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd2
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd2
-rw-r--r--addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd12
13 files changed, 548 insertions, 147 deletions
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd
index df14c0b..53d8927 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_break_counter.gd
@@ -1,20 +1,21 @@
-## Used by the intpreter to track the depth of a `break` statement invoked in a dialog.
-extends Object
+## Used by the intpreter to track dialog `break` propagation state.
class_name ESCBreakCounter
+extends Object
var _levels_left: int = 0:
set = set_levels_left,
get = get_levels_left
+var _resume_parent_dialog: bool = false
-## Sets the depth from the root of the dialog tree, with `levels == 0` at the root.[br]
+## Sets the remaining number of dialog levels to unwind before control should return to a parent dialog, where `levels == 0` means the unwind has reached the parent dialog that should resume.[br]
## [br]
## #### Parameters[br]
## [br]
## | Name | Type | Description | Required? |[br]
## |:-----|:-----|:------------|:----------|[br]
-## |levels|`int`|Number of dialog nesting levels remaining (0 represents the root).|yes|[br]
+## |levels|`int`|Number of dialog nesting levels still to exit before resuming a parent dialog.|yes|[br]
## [br]
## #### Returns[br]
## [br]
@@ -23,7 +24,7 @@ func set_levels_left(levels: int) -> void:
_levels_left = levels
-## Gets the depth from the root of the dialog tree, with `levels == 0` at the root.[br]
+## Gets the remaining number of dialog levels to unwind before control should return to a parent dialog, where `levels == 0` means the unwind has reached the parent dialog that should resume.[br]
## [br]
## #### Parameters[br]
## [br]
@@ -31,12 +32,68 @@ func set_levels_left(levels: int) -> void:
## [br]
## #### Returns[br]
## [br]
-## Returns a `int` value. (`int`)
+## Returns an `int` value.
func get_levels_left() -> int:
return _levels_left
-## Decrements by one the depth from the root of the dialog tree, with `levels == 0` at the root.[br]
+## Returns whether more dialog levels still need to be exited before reaching the parent dialog that should resume.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns `true` if additional dialog levels must still be unwound, otherwise `false`. (`bool`)
+func has_levels_left() -> bool:
+ return _levels_left > 0
+
+
+## Advances the dialog-break state upward by one dialog level.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func advance_up_one_level() -> void:
+ if _levels_left > 0:
+ _levels_left -= 1
+
+ if _levels_left == 0:
+ _resume_parent_dialog = true
+
+
+## Marks the dialog-break state so the receiving dialog frame resumes its own option loop instead of concluding.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns nothing.
+func mark_resume_parent_dialog() -> void:
+ _resume_parent_dialog = true
+
+
+## Returns whether the receiving dialog frame should resume its option loop.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns `true` if the parent dialog should resume, otherwise `false`. (`bool`)
+func should_resume_parent_dialog() -> bool:
+ return _resume_parent_dialog
+
+
+## Clears the resume-parent-dialog state after it has been consumed by a dialog frame.[br]
## [br]
## #### Parameters[br]
## [br]
@@ -45,5 +102,5 @@ func get_levels_left() -> int:
## #### Returns[br]
## [br]
## Returns nothing.
-func dec_levels_left() -> void:
- _levels_left = _levels_left - 1
+func consume_parent_resume() -> void:
+ _resume_parent_dialog = false
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd
index d63c52b..f89bed9 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_environment.gd
@@ -2,8 +2,8 @@
##
## Scoping is implemented using this class, including traditional scoping rules.
## Scope binding and resolution are handled in this class.
-extends Object
class_name ESCEnvironment
+extends Object
var _enclosing: # ESCEnvironment
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd
index 0e8bda8..ee94810 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_expr.gd
@@ -3,8 +3,8 @@
## All inheriting classes must implement an `accept` method to invoke the visitor.
## For more information on the "Visitor" pattern, see:
## (Visitor Pattern)[https://en.wikipedia.org/wiki/Visitor_pattern]
-extends RefCounted
class_name ESCGrammarExpr
+extends RefCounted
## Method that invokes another method in the visitor against this, the implementing class.[br]
@@ -18,5 +18,5 @@ class_name ESCGrammarExpr
## #### Returns[br]
## [br]
## Returns nothing.
-func accept(visitor):
+func accept(_visitor):
pass
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd
index 1793f0e..126e8cc 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmt.gd
@@ -3,8 +3,8 @@
## All inheriting classes must implement an `accept` method to invoke the visitor.
## For more information on the "Visitor" pattern, see:
## (Visitor Pattern)[https://en.wikipedia.org/wiki/Visitor_pattern]
-extends RefCounted
class_name ESCGrammarStmt
+extends RefCounted
## Method that invokes another method in the visitor against this, the implementing class.[br]
@@ -18,5 +18,5 @@ class_name ESCGrammarStmt
## #### Returns[br]
## [br]
## Returns nothing.
-func accept(visitor):
+func accept(_visitor):
pass
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd
index 2e9c575..07c197c 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_grammar_stmts.gd
@@ -154,6 +154,27 @@ class Event extends ESCGrammarStmt:
## [br]
signal interrupted
+ ## Valid event flags[br]
+ ##[br]
+ ## * TK: stands for "telekinetic". It means the player won't walk over to
+ ## the item to say the line.[br]
+ ## * NO_TT: stands for "No tooltip". It hides the tooltip for the duration of
+ ## the event. Probably not very useful, because events having multiple
+ ## say commands in them are automatically hidden.[br]
+ ## * NO_UI: stands for "No User Inteface". It hides the UI for the duration of
+ ##  the event. Useful when you want something to look like a cut scene but not
+ ## disable input for skipping dialog.[br]
+ ## * NO_SAVE: disables saving. Use this in cut scenes and anywhere a
+ ## badly-timed autosave would leave your game in a messed-up state.
+ enum {
+ FLAG_TK = 1,
+ FLAG_NO_TT = 2,
+ FLAG_NO_UI = 4,
+ FLAG_NO_SAVE = 8
+ }
+
+ var source: String = "" # TODO: Make proper use of this
+
var _name: ESCToken:
get = get_name
var _target: ESCGrammarExprs.Literal:
@@ -175,28 +196,6 @@ class Event extends ESCGrammarStmt:
var _is_interrupted: bool = false:
get = is_interrupted
- var source: String = "" # TODO: Make proper use of this
-
-
- ## Valid event flags[br]
- ##[br]
- ## * TK: stands for "telekinetic". It means the player won't walk over to
- ## the item to say the line.[br]
- ## * NO_TT: stands for "No tooltip". It hides the tooltip for the duration of
- ## the event. Probably not very useful, because events having multiple
- ## say commands in them are automatically hidden.[br]
- ## * NO_UI: stands for "No User Inteface". It hides the UI for the duration of
- ##  the event. Useful when you want something to look like a cut scene but not
- ## disable input for skipping dialog.[br]
- ## * NO_SAVE: disables saving. Use this in cut scenes and anywhere a
- ## badly-timed autosave would leave your game in a messed-up state.
- enum {
- FLAG_TK = 1,
- FLAG_NO_TT = 2,
- FLAG_NO_UI = 4,
- FLAG_NO_SAVE = 8
- }
-
## Initialization method. Must be called after instantiation.[br]
## [br]
@@ -784,6 +783,8 @@ class Stop extends ESCGrammarStmt:
## say("worker", "Because I work here.")[br]
##```
class DialogOption extends ESCGrammarStmt:
+ var _translation_key: String:
+ get = get_translation_key
var _option: ESCGrammarExpr:
get = get_option
var _condition: ESCGrammarExpr:
@@ -798,6 +799,7 @@ class DialogOption extends ESCGrammarStmt:
## [br]
## | Name | Type | Description | Required? |[br]
## |:-----|:-----|:------------|:----------|[br]
+ ## |translation_key|`String`|an optional translation key for the option text|yes|[br]
## |option|`ESCGrammarExpr`|the expression whose results will be displayed on screen as an option to be selected alongside any other options at the current level|yes|[br]
## |condition|`ESCGrammarExpr`|an optional condition to be evaluated that is used to determine whether the option should be displayed at all|yes|[br]
## |body|`ESCGrammarStmt`|the block of statements to be executed should this dialog option be selected|yes|[br]
@@ -805,12 +807,26 @@ class DialogOption extends ESCGrammarStmt:
## #### Returns[br]
## [br]
## Returns nothing.
- func init(option: ESCGrammarExpr, condition: ESCGrammarExpr, body: ESCGrammarStmt):
+ func init(translation_key: String, option: ESCGrammarExpr, condition: ESCGrammarExpr, body: ESCGrammarStmt):
+ _translation_key = translation_key
_option = option
_condition = condition
_body = body
+ ## The translation key for this option, if one exists.[br]
+ ## [br]
+ ## #### Parameters[br]
+ ## [br]
+ ## None.
+ ## [br]
+ ## #### Returns[br]
+ ## [br]
+ ## Returns the translation key for this option, if one exists. (`String`)
+ func get_translation_key() -> String:
+ return _translation_key
+
+
## The option expression.[br]
## [br]
## #### Parameters[br]
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd
index 0944486..2097890 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd
@@ -1,6 +1,6 @@
## The actual interpreter that processes a parsed ASHES script.
-extends RefCounted
class_name ESCInterpreter
+extends RefCounted
## Used to represent the current player character in scripts.
@@ -26,6 +26,7 @@ var _locals: Dictionary = {}
# Still, this should be tested extensively, and, if at all possible, with multiple
# concurrent events running.
var _current_event: ESCGrammarStmts.Event
+var _dialog_depth: int = 0
# While most of the time we only run a single event at a time, it is possible for
# multiple events to
@@ -34,10 +35,12 @@ var _event_stack: Array = []
var _builtin_functions: Array = [
"print"
]
+var _channel_name: String = ""
-func _init(callables: Array, globals: Dictionary):
+func _init(callables: Array, globals: Dictionary, channel_name: String = ""):
_globals = ESCEnvironment.new()
+ _channel_name = channel_name
for callable in callables:
_globals.define(callable.get_command_name(), callable)
@@ -77,6 +80,19 @@ func get_global_values() -> Dictionary:
return _globals.get_values()
+## Gets the event-manager channel this interpreter is executing on.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## None.
+## [br]
+## #### Returns[br]
+## [br]
+## Returns the channel name associated with this interpreter run. (`String`)
+func get_channel_name() -> String:
+ return _channel_name
+
+
## Resets the interpreter, specifically any locally-scoped variables.[br]
## [br]
## #### Parameters[br]
@@ -253,13 +269,15 @@ func visit_call_expr(expr: ESCGrammarExprs.Call):
self,
"Can only call valid commands."
)
- else:
- return _handle_builtin_function(callee, args)
+ return ESCExecution.RC_ERROR
+
+ return _handle_builtin_function(callee, args)
var command = ESCCommand.new()
command.parameters = args
command.name = callee.get_command_name()
command.parser_token = expr.get_paren_token()
+ command.channel_name = _channel_name
var rc = ESCExecution.RC_OK
@@ -282,11 +300,12 @@ func _handle_builtin_function(fn_name: String, args: Array) -> int:
match fn_name:
'print':
- if args.size() > 1:
+ if args.size() != 1:
escoria.logger.error(
self,
- "'print' only takes one argument"
+ "'print' expects exactly one argument"
)
+ return rc
_print(args)
rc = ESCExecution.RC_OK
@@ -315,13 +334,13 @@ func _print(value):
func visit_if_stmt(stmt: ESCGrammarStmts.If):
if _is_truthy(await _evaluate(stmt.get_condition())):
return await _execute(stmt.get_then_branch())
- else:
- for branch in stmt.get_elif_branches():
- if _is_truthy(await _evaluate(branch.get_condition())):
- return await _execute(branch)
- if stmt.get_else_branch():
- return await _execute(stmt.get_else_branch())
+ for branch in stmt.get_elif_branches():
+ if _is_truthy(await _evaluate(branch.get_condition())):
+ return await _execute(branch)
+
+ if stmt.get_else_branch():
+ return await _execute(stmt.get_else_branch())
return null
@@ -344,6 +363,9 @@ func visit_while_stmt(stmt: ESCGrammarStmts.While):
if ret is ESCGrammarStmts.Break:
break
+ if _is_terminal_control_flow(ret):
+ return ret
+
return null
@@ -358,10 +380,23 @@ func visit_while_stmt(stmt: ESCGrammarStmts.While):
## #### Returns[br]
## [br]
## Returns nothing.
-func visit_pass_stmt(stmt: ESCGrammarStmts.Pass):
+func visit_pass_stmt(_stmt: ESCGrammarStmts.Pass):
pass
+func _is_terminal_control_flow(ret) -> bool:
+ if ret is ESCGrammarStmts.Stop \
+ or ret is ESCGrammarStmts.Done \
+ or ret is ESCBreakCounter:
+ return true
+
+ if typeof(ret) == TYPE_INT:
+ return ret == ESCExecution.RC_ERROR \
+ or ret == ESCExecution.RC_INTERRUPTED
+
+ return false
+
+
## Executes code relevant to interpreting a `stop` statement. Relevant only to dialogs.[br]
## [br]
## #### Parameters[br]
@@ -438,14 +473,76 @@ func visit_global_stmt(stmt: ESCGrammarStmts.Global):
func visit_dialog_stmt(stmt: ESCGrammarStmts.Dialog):
var dialog: ESCDialog = ESCDialog.new()
var rc = ESCExecution.RC_OK
+ var dialog_args := stmt.get_args()
+ var dialog_options := stmt.get_options()
+
+ _dialog_depth += 1
+ dialog.authored_options_count = dialog_options.size()
+
+ if dialog_args.size() > 0:
+ var avatar = await _evaluate(dialog_args[0])
+
+ if avatar != null and not avatar is String and not avatar is StringName:
+ escoria.logger.error(
+ self,
+ "Dialog avatar must evaluate to a String."
+ )
+ _dialog_depth -= 1
+ return ESCExecution.RC_ERROR
+
+ dialog.avatar = str(avatar)
+
+ if dialog_args.size() > 1:
+ var timeout = await _evaluate(dialog_args[1])
+
+ if not timeout is int and not timeout is float:
+ escoria.logger.error(
+ self,
+ "Dialog timeout must evaluate to a number."
+ )
+ _dialog_depth -= 1
+ return ESCExecution.RC_ERROR
+
+ if timeout < 0:
+ escoria.logger.error(
+ self,
+ "Dialog timeout must be greater than or equal to 0."
+ )
+ _dialog_depth -= 1
+ return ESCExecution.RC_ERROR
+
+ dialog.timeout = int(timeout)
+
+ if dialog_args.size() > 2:
+ var timeout_option = await _evaluate(dialog_args[2])
+
+ if not timeout_option is int and not timeout_option is float:
+ escoria.logger.error(
+ self,
+ "Dialog timeout option must evaluate to a number."
+ )
+ _dialog_depth -= 1
+ return ESCExecution.RC_ERROR
+
+ if timeout_option < 0:
+ escoria.logger.error(
+ self,
+ "Dialog timeout option must be greater than or equal to 0."
+ )
+ _dialog_depth -= 1
+ return ESCExecution.RC_ERROR
+
+ dialog.timeout_option = int(timeout_option)
while true:
dialog.options = []
- for dialog_option in stmt.get_options():
+ for index in range(dialog_options.size()):
+ var dialog_option = dialog_options[index]
var option: ESCDialogOption = ESCDialogOption.new()
- # TODO: Translation keys
option.source_option = dialog_option
+ option.source_option_index = index + 1
+ option.translation_key = dialog_option.get_translation_key()
option.option = await _evaluate(dialog_option.get_option())
if dialog_option.get_condition():
@@ -453,12 +550,23 @@ func visit_dialog_stmt(stmt: ESCGrammarStmts.Dialog):
else:
option.set_is_valid(true)
- dialog.options.append(option)
+ # Dialog execution should stop once no valid options remain. Keep only
+ # currently valid options in the presented list so an exhausted dialog
+ # concludes instead of re-running forever with unselectable entries.
+ if option.is_valid():
+ dialog.options.append(option)
if dialog.options.size() == 0:
break
- if dialog.is_valid() and not _current_event.is_interrupted():
+ # If the event was interrupted while a nested command or dialog body was
+ # active, stop the dialog frame immediately instead of looping and
+ # presenting options again.
+ if _current_event and _current_event.is_interrupted():
+ _dialog_depth -= 1
+ return ESCExecution.RC_INTERRUPTED
+
+ if dialog.is_valid():
var chosen_option = await dialog.run()
if chosen_option:
@@ -468,22 +576,73 @@ func visit_dialog_stmt(stmt: ESCGrammarStmts.Dialog):
"Chosen dialog option (%s) was completed." % chosen_option
)
- if execute_ret is ESCGrammarStmts.Break:
- var break_tracker: ESCBreakCounter = ESCBreakCounter.new()
+ # Dialog frames must propagate terminal runtime errors the same
+ # way normal blocks do. Otherwise an invalid command or similar
+ # failure inside an option body is treated like normal completion
+ # and the dialog loop re-displays the same options indefinitely.
+ if typeof(execute_ret) == TYPE_INT and execute_ret == ESCExecution.RC_ERROR:
+ _dialog_depth -= 1
+ return ESCExecution.RC_ERROR
+ # An interruption can happen while the chosen option body is still
+ # running, so check again after it returns before processing normal
+ # dialog control flow like `break`, `done`, or `stop`.
+ if _current_event and _current_event.is_interrupted():
+ _dialog_depth -= 1
+ return ESCExecution.RC_INTERRUPTED
+
+ if execute_ret is ESCGrammarStmts.Break:
+ var levels_left := 0
if execute_ret.get_levels():
- break_tracker.set_levels_left(await _evaluate(execute_ret.get_levels()) - 1)
+ levels_left = await _evaluate(execute_ret.get_levels()) - 1
+
+ # A top-level dialog consumes `break` by concluding the current dialog.
+ if _dialog_depth == 1:
+ break
+
+ # Nested dialogs propagate explicit break state upward so parent
+ # dialog frames can decide whether to keep unwinding or resume.
+ var break_tracker: ESCBreakCounter = ESCBreakCounter.new()
+ if levels_left > 0:
+ break_tracker.set_levels_left(levels_left)
else:
- break_tracker.set_levels_left(0)
+ break_tracker.mark_resume_parent_dialog()
+ _dialog_depth -= 1
return break_tracker
- elif execute_ret is ESCGrammarStmts.Done:
+ if execute_ret is ESCGrammarStmts.Done:
+ _dialog_depth -= 1
+
+ if _dialog_depth == 0:
+ return rc
+
return execute_ret
- elif execute_ret is ESCBreakCounter:
- if execute_ret.get_levels_left() > 0:
- execute_ret.dec_levels_left()
+
+ if execute_ret is ESCGrammarStmts.Stop:
+ # `stop` aborts the entire event, so dialog frames must pass it through.
+ _dialog_depth -= 1
+ return execute_ret
+
+ if execute_ret is ESCBreakCounter:
+ if execute_ret.has_levels_left():
+ # Once the unwind reaches the top-most dialog, the remaining
+ # levels are consumed by concluding that dialog entirely.
+ if _dialog_depth == 1:
+ break
+
+ execute_ret.advance_up_one_level()
+ _dialog_depth -= 1
return execute_ret
+ if execute_ret.should_resume_parent_dialog():
+ # The requested dialog levels have been exited, so this frame
+ # resumes presenting its own options instead of concluding.
+ execute_ret.consume_parent_resume()
+ continue
+
+ break
+
+ _dialog_depth -= 1
return rc
@@ -498,7 +657,7 @@ func visit_dialog_stmt(stmt: ESCGrammarStmts.Dialog):
## #### Returns[br]
## [br]
## Returns nothing.
-func visit_dialog_option_stmt(stmt: ESCGrammarStmts.DialogOption):
+func visit_dialog_option_stmt(_stmt: ESCGrammarStmts.DialogOption):
pass
@@ -623,18 +782,28 @@ func visit_binary_expr(expr: ESCGrammarExprs.Binary):
return not _is_equal(left_part, right_part)
ESCTokenType.TokenType.GREATER:
var check = _check_are_numbers(left_part, expr.get_operator(), right_part)
+ if not check:
+ return null
return left_part > right_part
ESCTokenType.TokenType.GREATER_EQUAL:
var check = _check_are_numbers(left_part, expr.get_operator(), right_part)
+ if not check:
+ return null
return left_part >= right_part
ESCTokenType.TokenType.LESS:
var check = _check_are_numbers(left_part, expr.get_operator(), right_part)
+ if not check:
+ return null
return left_part < right_part
ESCTokenType.TokenType.LESS_EQUAL:
var check = _check_are_numbers(left_part, expr.get_operator(), right_part)
+ if not check:
+ return null
return left_part <= right_part
ESCTokenType.TokenType.MINUS:
var check = _check_are_numbers(left_part, expr.get_operator(), right_part)
+ if not check:
+ return null
return left_part - right_part
ESCTokenType.TokenType.PLUS:
var check = _check_are_numbers(left_part, expr.get_operator(), right_part, false)
@@ -653,9 +822,13 @@ func visit_binary_expr(expr: ESCGrammarExprs.Binary):
)
ESCTokenType.TokenType.SLASH:
var check = _check_are_numbers(left_part, expr.get_operator(), right_part)
+ if not check:
+ return null
return left_part / right_part
ESCTokenType.TokenType.STAR:
var check = _check_are_numbers(left_part, expr.get_operator(), right_part)
+ if not check:
+ return null
return left_part * right_part
return null
@@ -680,6 +853,8 @@ func visit_unary_expr(expr: ESCGrammarExprs.Unary):
return not _is_truthy(right_part)
ESCTokenType.TokenType.MINUS:
var check = _check_is_number(right_part, expr.get_operator())
+ if not check:
+ return null
return -right_part
return null
@@ -793,8 +968,8 @@ func look_up_variable(name: ESCToken, expr: ESCGrammarExpr):
if distance == -1:
return _globals.get_value(name)
- else:
- return _environment.get_at(distance, name.get_lexeme())
+
+ return _environment.get_at(distance, name.get_lexeme())
# Private methods
@@ -860,10 +1035,18 @@ func _execute_block(statements: Array, env: ESCEnvironment):
for stmt in statements:
ret = await _execute(stmt)
- if ret is ESCGrammarStmts.Break \
- or ret is ESCGrammarStmts.Done \
- or ret is ESCGrammarStmts.Stop:
+ # If the running event was interrupted while this statement was active,
+ # stop the block immediately so later statements do not continue running.
+ if _current_event and _current_event.is_interrupted():
+ _environment = previous_env
+ return ESCExecution.RC_INTERRUPTED
+ if ret is ESCGrammarStmts.Break:
+ _environment = previous_env
+ return ret
+
+ if _is_terminal_control_flow(ret):
+ _environment = previous_env
return ret
# TODO: Proper error handling per statement?
@@ -925,12 +1108,12 @@ func _check_at_least_one_string(value_1, value_2):
return typeof(value_1) == TYPE_STRING || typeof(value_2) == TYPE_STRING
-func _on_global_changed(key: String, old_value, new_value) -> void:
+func _on_global_changed(key: String, _old_value, new_value) -> void:
# Shoehorn this in as an adapter
var token: ESCToken = ESCToken.new()
token.init(ESCTokenType.TokenType.IDENTIFIER, key, null, "", -1, "")
if _globals.get_values().has(key):
_globals.assign(token, new_value)
- elif escoria.save_manager.is_loading_game:
+ else:
_globals.define(key, new_value)
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd
index 4205f92..24f6c66 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter_factory.gd
@@ -1,6 +1,10 @@
## Factory class for producing an ASHES interpreter.
-extends RefCounted
class_name ESCInterpreterFactory
+extends RefCounted
+
+const ESC_INTERPRETER_SCRIPT := preload(
+ "res://addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd"
+)
static var _interpreter: ESCInterpreter = null
@@ -15,16 +19,64 @@ static var _interpreter: ESCInterpreter = null
## #### Returns[br]
## [br]
## Returns a `ESCInterpreter` value. (`ESCInterpreter`)
-static func create_interpreter() -> ESCInterpreter:
- if not _interpreter:
- _interpreter = load("res://addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd").new(ESCCompiler.load_commands(), ESCCompiler.load_globals())
- return _interpreter
+static func create_interpreter(channel_name: String = "") -> ESCInterpreter:
+ return create_utility_interpreter(channel_name)
+
- _interpreter = load("res://addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd").new([], _interpreter.get_global_values())
+## Produces an interpreter for non-runtime utility work such as resolving or
+## evaluating statements outside the event manager.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |channel_name|`String`|Optional channel name to assign to the created interpreter.|no|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns a cached-or-reseeded `ESCInterpreter` instance for utility usage. (`ESCInterpreter`)
+static func create_utility_interpreter(channel_name: String = "") -> ESCInterpreter:
+ if not _interpreter:
+ _interpreter = ESC_INTERPRETER_SCRIPT.new(
+ ESCCompiler.load_commands(),
+ ESCCompiler.load_globals(),
+ channel_name
+ )
+ else:
+ _interpreter = ESC_INTERPRETER_SCRIPT.new(
+ [],
+ _interpreter.get_global_values(),
+ channel_name
+ )
return _interpreter
+## Produces a fresh interpreter intended for event-manager runtime execution.[br]
+## [br]
+## #### Parameters[br]
+## [br]
+## | Name | Type | Description | Required? |[br]
+## |:-----|:-----|:------------|:----------|[br]
+## |channel_name|`String`|Optional channel name to assign to the created interpreter.|no|[br]
+## [br]
+## #### Returns[br]
+## [br]
+## Returns a newly-created `ESCInterpreter` instance for runtime usage. (`ESCInterpreter`)
+static func create_runtime_interpreter(channel_name: String = "") -> ESCInterpreter:
+ var runtime_globals := ESCCompiler.load_globals()
+
+ if not Engine.is_editor_hint() and escoria and escoria.globals_manager:
+ runtime_globals.merge(escoria.globals_manager.get_globals(), true)
+
+ var interpreter := ESC_INTERPRETER_SCRIPT.new(
+ ESCCompiler.load_commands(),
+ runtime_globals,
+ channel_name
+ )
+ return interpreter
+
+
static func reset_interpreter() -> void:
if is_instance_valid(_interpreter):
_interpreter.cleanup()
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd
index 5fd01a3..1bb5b19 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parse_error.gd
@@ -1,3 +1,3 @@
## Class to represent an error that occured during parsing.
-extends Object
class_name ESCParseError
+extends Object
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd
index b88adcb..6229e34 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_parser.gd
@@ -4,8 +4,8 @@
## Note that the vast majority of this class consists of (effectively) private methods
## in order to facilitate encapsulation, but if you're interested in how the parser
## is implemented, [check out the class in GitHub](https://github.com/godot-escoria/escoria-demo-game/blob/main/addons/escoria-core/game/core-scripts/esc/compiler/esc_interpreter.gd)
-extends RefCounted
class_name ESCParser
+extends RefCounted
var _tokens: Array
@@ -52,48 +52,56 @@ func parse() -> Array:
var statements: Array = []
while not _at_end():
- statements.append(_declaration())
+ var declaration = _declaration()
+ if declaration != null:
+ statements.append(declaration)
return statements
func _declaration() -> ESCGrammarStmt:
- var retStmt
+ while _match(ESCTokenType.TokenType.NEWLINE) or _match(ESCTokenType.TokenType.DEDENT):
+ pass
+
+ if _at_end():
+ return null
+
+ var ret_stmt
if _match(ESCTokenType.TokenType.COLON):
- retStmt = _event_declaration()
+ ret_stmt = _event_declaration()
- if retStmt is ESCParseError:
+ if ret_stmt is ESCParseError:
_synchronize()
return null
- else:
- return retStmt
+
+ return ret_stmt
if _match(ESCTokenType.TokenType.VAR):
- retStmt = _var_declaration()
+ ret_stmt = _var_declaration()
- if retStmt is ESCParseError:
+ if ret_stmt is ESCParseError:
_synchronize()
return null
- else:
- return retStmt
+
+ return ret_stmt
if _match(ESCTokenType.TokenType.GLOBAL):
- retStmt = _global_declaration()
+ ret_stmt = _global_declaration()
- if retStmt is ESCParseError:
+ if ret_stmt is ESCParseError:
_synchronize()
return null
- else:
- return retStmt
- retStmt = _statement()
+ return ret_stmt
+
+ ret_stmt = _statement()
- if retStmt is ESCParseError:
+ if ret_stmt is ESCParseError:
_synchronize()
return null
- return retStmt
+ return ret_stmt
func _event_declaration():
@@ -157,8 +165,8 @@ func _event_declaration():
ret.init(name, target, flags, body, _associated_object_global_id)
return ret
- else:
- return _error(_peek(), "Expected block after event declaration for '%s'. Code blocks require tab(s) at the start of a line." % name.get_lexeme())
+
+ return _error(_peek(), "Expected block after event declaration for '%s'. Code blocks require tab(s) at the start of a line." % name.get_lexeme())
func _literal_from_identifier(expr: ESCGrammarExprs.Variable) -> ESCGrammarExprs.Literal:
@@ -314,21 +322,25 @@ func _while_statement():
var condition = _expression()
if condition is ESCParseError:
+ _loop_level -= 1
return condition
var colon_token = _consume(ESCTokenType.TokenType.COLON, "Expect ':' after condition.")
if colon_token is ESCParseError:
+ _loop_level -= 1
return colon_token
var consume = _consume_new_block_start("start of while loop")
if consume:
+ _loop_level -= 1
return consume
var block = _block()
if block is ESCParseError:
+ _loop_level -= 1
return block
var body = ESCGrammarStmts.Block.new()
@@ -372,6 +384,7 @@ func _dialog_statement():
var arg = _expression()
if arg is ESCParseError:
+ _dialog_level -= 1
return arg
args.append(arg)
@@ -382,14 +395,32 @@ func _dialog_statement():
var consume = _consume(ESCTokenType.TokenType.RIGHT_PAREN, "Expect ')' after start dialog arguments.")
if consume is ESCParseError:
+ _dialog_level -= 1
return consume
+ elif not _check(ESCTokenType.TokenType.NEWLINE):
+ while true:
+ var arg = _expression()
+
+ if arg is ESCParseError:
+ _dialog_level -= 1
+ return arg
+
+ args.append(arg)
+
+ if _check(ESCTokenType.TokenType.NEWLINE):
+ break
+
+ if _match(ESCTokenType.TokenType.COMMA):
+ continue
if args.size() > 3:
+ _dialog_level -= 1
return _error(_peek(), "Start dialog cannot have more than 3 arguments.")
var consume = _consume_new_block_start("dialog start")
if consume is ESCParseError:
+ _dialog_level -= 1
return consume
var options: Array = []
@@ -398,6 +429,7 @@ func _dialog_statement():
var dialog_option = _dialog_option_statement()
if dialog_option is ESCParseError:
+ _dialog_level -= 1
return dialog_option
options.append(dialog_option)
@@ -433,6 +465,14 @@ func _dialog_option_statement():
if consume is ESCParseError:
return consume
+ var translation_key := ""
+
+ if _match_in_order([
+ ESCTokenType.TokenType.IDENTIFIER,
+ ESCTokenType.TokenType.COLON
+ ]):
+ translation_key = _tokens[_current - 2].get_lexeme()
+
var expr = _expression()
if expr is ESCParseError:
@@ -448,8 +488,14 @@ func _dialog_option_statement():
consume = _consume(ESCTokenType.TokenType.RIGHT_SQUARE, "Expect ']' after dialog option condition")
+ if consume is ESCParseError:
+ return consume
+
consume = _consume_new_block_start("dialog option")
+ if consume is ESCParseError:
+ return consume
+
var block_stmts = _block()
if block_stmts is ESCParseError:
@@ -459,7 +505,7 @@ func _dialog_option_statement():
block.init(block_stmts)
var option: ESCGrammarStmts.DialogOption = ESCGrammarStmts.DialogOption.new()
- option.init(expr, condition, block)
+ option.init(translation_key, expr, condition, block)
return option
@@ -512,7 +558,8 @@ func _block():
if decl is ESCParseError:
return decl
- statements.append(decl)
+ if decl != null:
+ statements.append(decl)
_consume(ESCTokenType.TokenType.DEDENT, "Expected DEDENT after block.")
return statements
@@ -536,7 +583,8 @@ func _assignment():
var ret = ESCGrammarExprs.Assign.new()
ret.init(name, value)
return ret
- elif expr is ESCGrammarExprs.Get:
+
+ if expr is ESCGrammarExprs.Get:
var ret = ESCGrammarExprs.Set.new()
ret.init(expr.get_object(), expr.get_name(), value)
return ret
@@ -723,15 +771,15 @@ func _is_checking():
var ret = ESCGrammarExprs.Is.new()
ret.init(expr, null, _previous())
return ret
- else:
- var state_expr = _expression()
- if state_expr is ESCParseError:
- return state_expr
+ var state_expr = _expression()
- var ret = ESCGrammarExprs.Is.new()
- ret.init(expr, state_expr, null)
- return ret
+ if state_expr is ESCParseError:
+ return state_expr
+
+ var ret = ESCGrammarExprs.Is.new()
+ ret.init(expr, state_expr, null)
+ return ret
return expr
@@ -777,6 +825,9 @@ func _finish_call(callee: ESCGrammarExpr):
var paren = _consume(ESCTokenType.TokenType.RIGHT_PAREN, "Expect ')' after arguments.")
+ if paren is ESCParseError:
+ return paren
+
#if paren.get_type() != ESCTokenType.TokenType.NEWLINE:
# return _error(ESCTokenType.TokenType.NEWLINE, "Expect NEWLINE after arguments.")
@@ -817,7 +868,13 @@ func _primary():
if expr is ESCParseError:
return expr
- _consume(ESCTokenType.TokenType.RIGHT_PAREN, "Expect ')' after expression.")
+ var paren = _consume(
+ ESCTokenType.TokenType.RIGHT_PAREN,
+ "Expect ')' after expression."
+ )
+
+ if paren is ESCParseError:
+ return paren
var ret = ESCGrammarExprs.Grouping.new()
ret.init(expr)
@@ -826,30 +883,52 @@ func _primary():
return _error(_peek(), "Expect expression.")
-func _var_declaration() -> ESCGrammarStmt:
+func _var_declaration():
var name = _consume(ESCTokenType.TokenType.IDENTIFIER, "Expect variable name.")
var initializer: ESCGrammarExpr = null
if _match(ESCTokenType.TokenType.EQUAL):
- initializer = _expression()
+ var initializer_expr = _expression()
+
+ if initializer_expr is ESCParseError:
+ return initializer_expr
+
+ initializer = initializer_expr
+
+ var newline = _consume(
+ ESCTokenType.TokenType.NEWLINE,
+ "Expect newline after variable declaration."
+ )
- _consume(ESCTokenType.TokenType.NEWLINE, "Expect newline after variable declaration.")
+ if newline is ESCParseError:
+ return newline
var ret = ESCGrammarStmts.Var.new()
ret.init(name, initializer)
return ret
-func _global_declaration() -> ESCGrammarStmt:
+func _global_declaration():
var name = _consume(ESCTokenType.TokenType.IDENTIFIER, "Expect global variable name.")
var initializer: ESCGrammarExpr = null
if _match(ESCTokenType.TokenType.EQUAL):
- initializer = _expression()
+ var initializer_expr = _expression()
- _consume(ESCTokenType.TokenType.NEWLINE, "Expect newline after global variable declaration.")
+ if initializer_expr is ESCParseError:
+ return initializer_expr
+
+ initializer = initializer_expr
+
+ var newline = _consume(
+ ESCTokenType.TokenType.NEWLINE,
+ "Expect newline after global variable declaration."
+ )
+
+ if newline is ESCParseError:
+ return newline
var ret = ESCGrammarStmts.Global.new()
ret.init(name, initializer)
@@ -864,11 +943,11 @@ func _peek() -> ESCToken:
return _tokens[_current]
-func _match(tokenTypes) -> bool:
- if not tokenTypes is Array:
- tokenTypes = [tokenTypes]
+func _match(token_types) -> bool:
+ if not token_types is Array:
+ token_types = [token_types]
- for type in tokenTypes:
+ for type in token_types:
if _check(type):
_advance()
return true
@@ -876,12 +955,15 @@ func _match(tokenTypes) -> bool:
return false
-func _match_in_order(tokenTypes) -> bool:
- if not tokenTypes is Array:
- tokenTypes = [tokenTypes]
+func _match_in_order(token_types) -> bool:
+ if not token_types is Array:
+ token_types = [token_types]
- for type in tokenTypes:
+ var start := _current
+
+ for type in token_types:
if not _check(type):
+ _current = start
return false
_advance()
@@ -889,21 +971,21 @@ func _match_in_order(tokenTypes) -> bool:
return true
-func _consume(tokenType, message: String):
- if _check(tokenType):
+func _consume(token_type, message: String):
+ if _check(token_type):
return _advance()
return _error(_peek(), message)
-func _check(tokenTypes) -> bool:
- if not tokenTypes is Array:
- tokenTypes = [tokenTypes]
+func _check(token_types) -> bool:
+ if not token_types is Array:
+ token_types = [token_types]
if _at_end():
return false
- for type in tokenTypes:
+ for type in token_types:
if _peek().get_type() == type:
return true
@@ -911,9 +993,9 @@ func _check(tokenTypes) -> bool:
# This turns the parser into an LL(2).
-func _check_next(tokenTypes) -> bool:
- if not tokenTypes is Array:
- tokenTypes = [tokenTypes]
+func _check_next(token_types) -> bool:
+ if not token_types is Array:
+ token_types = [token_types]
if _at_end():
return false
@@ -924,7 +1006,7 @@ func _check_next(tokenTypes) -> bool:
_current -= 1
return false
- for type in tokenTypes:
+ for type in token_types:
if _peek().get_type() == type:
_current -= 1
return true
@@ -959,16 +1041,27 @@ func _error(token: ESCToken, message: String) -> ESCParseError:
func _synchronize() -> void:
+ if _check(ESCTokenType.TokenType.DEDENT):
+ return
+
_advance()
while not _at_end():
- if _previous().get_type() == ESCTokenType.TokenType.NEWLINE:
+ if _previous().get_type() == ESCTokenType.TokenType.NEWLINE \
+ or _previous().get_type() == ESCTokenType.TokenType.DEDENT:
return
match _peek().get_type():
+ ESCTokenType.TokenType.COLON,\
+ ESCTokenType.TokenType.GLOBAL,\
ESCTokenType.TokenType.VAR,\
ESCTokenType.TokenType.IF,\
ESCTokenType.TokenType.WHILE,\
+ ESCTokenType.TokenType.PASS,\
+ ESCTokenType.TokenType.STOP,\
+ ESCTokenType.TokenType.QUESTION_BANG,\
+ ESCTokenType.TokenType.BREAK,\
+ ESCTokenType.TokenType.DONE,\
ESCTokenType.TokenType.RETURN:
return
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd
index 2b32b35..d356b85 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_resolver.gd
@@ -1,8 +1,8 @@
## Resolves script variables to ensure that scoping rules are obeyed.
##
## The class will throw errors if scoping rules are broken or otherwise not followed.
-extends RefCounted
class_name ESCResolver
+extends RefCounted
var _interpreter
@@ -136,7 +136,7 @@ func visit_while_stmt(stmt: ESCGrammarStmts.While):
## #### Returns[br]
## [br]
## Returns nothing.
-func visit_pass_stmt(stmt: ESCGrammarStmts.Pass):
+func visit_pass_stmt(_stmt: ESCGrammarStmts.Pass):
pass
@@ -151,7 +151,7 @@ func visit_pass_stmt(stmt: ESCGrammarStmts.Pass):
## #### Returns[br]
## [br]
## Returns nothing.
-func visit_stop_stmt(stmt: ESCGrammarStmts.Stop):
+func visit_stop_stmt(_stmt: ESCGrammarStmts.Stop):
pass
@@ -186,7 +186,7 @@ func visit_var_stmt(stmt: ESCGrammarStmts.Var):
## #### Returns[br]
## [br]
## Returns nothing.
-func visit_global_stmt(stmt: ESCGrammarStmts.Global):
+func visit_global_stmt(_stmt: ESCGrammarStmts.Global):
pass
@@ -237,7 +237,7 @@ func visit_break_stmt(stmt: ESCGrammarStmts.Break):
## #### Returns[br]
## [br]
## Returns nothing.
-func visit_done_stmt(stmt: ESCGrammarStmts.Done):
+func visit_done_stmt(_stmt: ESCGrammarStmts.Done):
pass
@@ -270,7 +270,7 @@ func visit_dialog_stmt(stmt: ESCGrammarStmts.Dialog):
## #### Returns[br]
## [br]
## Returns nothing.
-func visit_literal_expr(expr: ESCGrammarExprs.Literal):
+func visit_literal_expr(_expr: ESCGrammarExprs.Literal):
pass
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd
index 08642a9..6591c48 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_scanner.gd
@@ -3,8 +3,8 @@
## This is the first link in the ASHES compiler toolchain, and takes in ASHES
## script as text in order to provide a list of tokens to be handled by the
## ASHES parser.
-extends RefCounted
class_name ESCScanner
+extends RefCounted
## In ASHES, an identifier starting with `$` is interpreted as a lookup in
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd
index 46ba408..26dacce 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_script_builder.gd
@@ -1,7 +1,7 @@
## Convenience class to help build scripts in code for use with the ASHES compiler
## toolchain. Loosely follows the Builder pattern.
-extends RefCounted
class_name ESCScriptBuilder
+extends RefCounted
# Not really a "Builder" pattern per se, but helps with readability.
diff --git a/addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd b/addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd
index ec95d98..bd99d73 100644
--- a/addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd
+++ b/addons/escoria-core/game/core-scripts/esc/compiler/esc_token.gd
@@ -3,11 +3,11 @@
##
## This class contains any information required by the parser but also carries
## with it associated information useful for debugging and tracing purposes.
-extends RefCounted
class_name ESCToken
+extends RefCounted
-var _tokenType: int: # ESCScanner.TokenType enum
+var _token_type: int: # ESCScanner.TokenType enum
get = get_type
var _lexeme: String:
get = get_lexeme
@@ -38,8 +38,8 @@ var _filename: String:
## #### Returns[br]
## [br]
## Returns nothing.
-func init(tokenType: int, lexeme: String, literal, source: String, line: int, filename: String) -> void:
- _tokenType = tokenType
+func init(token_type: int, lexeme: String, literal, source: String, line: int, filename: String) -> void:
+ _token_type = token_type
_lexeme = lexeme
_literal = literal
_source = source
@@ -85,7 +85,7 @@ func get_source() -> String:
## [br]
## Returns the type of token this is; will be a value from `ESCTokenType.TokenType`. (`int`)
func get_type() -> int:
- return _tokenType
+ return _token_type
## The literal value of the token, if it exists.[br]
@@ -141,4 +141,4 @@ func get_filename() -> String:
func _to_string() -> String:
- return ESCTokenType.get_token_type_name(_tokenType) + " " + str(_lexeme) + " " + (str(_literal) if _literal else "")
+ return ESCTokenType.get_token_type_name(_token_type) + " " + str(_lexeme) + " " + (str(_literal) if _literal else "")