From e16d2a4342a4500d0838b7760423c722e15cf3b8 Mon Sep 17 00:00:00 2001 From: glx22 Date: Fri, 4 Aug 2023 21:49:25 +0200 Subject: [PATCH 1/4] Add: 'const' keyword to allow users to define new constants --- nml/ast/constant.py | 47 +++++++++++++++++++++++++++++++++++++++++++++ nml/parser.py | 8 +++++++- nml/tokens.py | 1 + 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 nml/ast/constant.py diff --git a/nml/ast/constant.py b/nml/ast/constant.py new file mode 100644 index 000000000..d7e15dbf1 --- /dev/null +++ b/nml/ast/constant.py @@ -0,0 +1,47 @@ +__license__ = """ +NML is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +NML is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with NML; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.""" + +from nml import expression, generic, global_constants +from nml.ast import base_statement + + +class Constant(base_statement.BaseStatement): + def __init__(self, name, value): + base_statement.BaseStatement.__init__(self, "constant", name.pos, False, False) + self.name = name + self.value = value + + def register_names(self): + if not isinstance(self.name, expression.Identifier): + raise generic.ScriptError("Constant name should be an identifier", self.name.pos) + if self.name.value in global_constants.constant_numbers: + raise generic.ScriptError("Redefinition of constant '{}'.".format(self.name.value), self.name.pos) + global_constants.constant_numbers[self.name.value] = self.value.reduce_constant( + global_constants.const_list + ).value + + def debug_print(self, indentation): + generic.print_dbg(indentation, "Constant") + generic.print_dbg(indentation + 2, "Name:") + self.name.debug_print(indentation + 4) + + generic.print_dbg(indentation + 2, "Value:") + self.value.debug_print(indentation + 4) + + def get_action_list(self): + return [] + + def __str__(self): + return "const {} = {};".format(self.name, self.value) diff --git a/nml/parser.py b/nml/parser.py index c303219ca..088b830eb 100644 --- a/nml/parser.py +++ b/nml/parser.py @@ -24,6 +24,7 @@ basecost, cargotable, conditional, + constant, deactivate, disable_item, error, @@ -153,7 +154,8 @@ def p_main_block(self, t): | snowline | engine_override | sort_vehicles - | basecost""" + | basecost + | constant""" t[0] = t[1] # @@ -839,3 +841,7 @@ def p_tilelayout_item_tile(self, t): def p_tilelayout_item_prop(self, t): "tilelayout_item : assignment" t[0] = t[1] + + def p_constant(self, t): + "constant : CONST expression EQ expression SEMICOLON" + t[0] = constant.Constant(t[2], t[4]) diff --git a/nml/tokens.py b/nml/tokens.py index d40b1c730..2591feaf5 100644 --- a/nml/tokens.py +++ b/nml/tokens.py @@ -61,6 +61,7 @@ "recolour_sprite": "RECOLOUR_SPRITE", "engine_override": "ENGINE_OVERRIDE", "sort": "SORT_VEHICLES", + "const": "CONST" } # fmt: on From e6b7ef45ab31d3c8f6f7b30049828c70472cabcc Mon Sep 17 00:00:00 2001 From: glx22 Date: Fri, 4 Aug 2023 22:32:55 +0200 Subject: [PATCH 2/4] Fix: delay item 'id' validation to allow use of user-defined constants --- nml/ast/item.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nml/ast/item.py b/nml/ast/item.py index 5a6bae83c..d12d59c7e 100644 --- a/nml/ast/item.py +++ b/nml/ast/item.py @@ -51,10 +51,7 @@ def __init__(self, params, body, pos): if self.feature.value in (0x08, 0x0C, 0x0E): raise generic.ScriptError("Defining item blocks for this feature is not allowed.", self.pos) self.name = params[1] if len(params) >= 2 else None - - self.id = params[2].reduce_constant(global_constants.const_list) if len(params) >= 3 else None - if isinstance(self.id, expression.ConstantNumeric) and self.id.value == -1: - self.id = None # id == -1 means default + self.id = params[2] if len(params) >= 3 else None if len(params) >= 4: if self.feature.value != 0x07: @@ -66,6 +63,11 @@ def __init__(self, params, body, pos): self.size = None def register_names(self): + if self.id is not None: + self.id = self.id.reduce_constant(global_constants.const_list) + if isinstance(self.id, expression.ConstantNumeric) and self.id.value == -1: + self.id = None # id == -1 means default + if self.name: if not isinstance(self.name, expression.Identifier): raise generic.ScriptError("Item parameter 2 'name' should be an identifier", self.pos) From a8ae18c9a51d9d600506c132edc034f1c72df33b Mon Sep 17 00:00:00 2001 From: glx22 Date: Fri, 4 Aug 2023 22:35:26 +0200 Subject: [PATCH 3/4] Fix: delay disable_item 'id' validations to allow use of user-defined constants --- nml/ast/disable_item.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/nml/ast/disable_item.py b/nml/ast/disable_item.py index 076265e17..f2edf10c5 100644 --- a/nml/ast/disable_item.py +++ b/nml/ast/disable_item.py @@ -39,18 +39,17 @@ def __init__(self, param_list, pos): "disable_item() requires between 1 and 3 parameters, encountered {:d}.".format(len(param_list)), pos ) self.feature = general.parse_feature(param_list[0]) + self.first_id = param_list[1] if len(param_list) > 1 else None + self.last_id = param_list[2] if len(param_list) > 2 else None - if len(param_list) > 1: - self.first_id = param_list[1].reduce_constant(global_constants.const_list) - else: - self.first_id = None + def pre_process(self): + if self.first_id is not None: + self.first_id = self.first_id.reduce_constant(global_constants.const_list) - if len(param_list) > 2: - self.last_id = param_list[2].reduce_constant(global_constants.const_list) + if self.last_id is not None: + self.last_id = self.last_id.reduce_constant(global_constants.const_list) if self.last_id.value < self.first_id.value: - raise generic.ScriptError("Last id to disable may not be lower than the first id.", pos) - else: - self.last_id = None + raise generic.ScriptError("Last id to disable may not be lower than the first id.", self.last_id.pos) def debug_print(self, indentation): generic.print_dbg(indentation, "Disable items, feature=" + str(self.feature.value)) From 0659ea917661e34c5d33fe3a9d8fd0600457e567 Mon Sep 17 00:00:00 2001 From: glx22 Date: Thu, 5 Oct 2023 21:51:04 +0200 Subject: [PATCH 4/4] Codechange: Use "const" in 030_house regression test --- regression/030_house.nml | 4 +- regression/expected/030_house.grf | Bin 14397 -> 14343 bytes regression/expected/030_house.nfo | 133 ++++++++++++++---------------- 3 files changed, 62 insertions(+), 75 deletions(-) diff --git a/regression/030_house.nml b/regression/030_house.nml index 023baa95f..ea65250d4 100644 --- a/regression/030_house.nml +++ b/regression/030_house.nml @@ -50,8 +50,8 @@ spriteset(brewery_spriteset_building) { tmpl_building_sprite(150, 60, 91, -60, "brewery_snow.png") } -default_palette = PALETTE_USE_DEFAULT; -/* Generic sprite layout that is used for all tiles of the building. Parmaeters: +const default_palette = PALETTE_USE_DEFAULT; +/* Generic sprite layout that is used for all tiles of the building. Parameters: * - building_sprite: offset in the brewery_spriteset_building spriteset to use. -1 to skip building sprite. * - with_smoke: Show smoke above the tile (also depends on animation state) */ spritelayout brewery_sprite_layout(building_sprite, with_smoke) { diff --git a/regression/expected/030_house.grf b/regression/expected/030_house.grf index bac652acb46a151890bd3a08c20ddc66a36580ca..85e0bf88f3024c9f6942a15047e8b12555fbe92d 100644 GIT binary patch delta 288 zcmdm6&|VswTxCQDs>G1fyx;eIDz;-Cj$$&dyu0iBbdhvl4Asl zPu|EXt`hFy>IfCDg^II+^zhbm)YXF3v)6AHV-sa$te>pI9t|Wrz~mD!>A)e*$i6v( zLz9^eCllVdp~H!tAc%)|!LD+l2h@v$>8@@}pY_{_q{w>eLAKNlnaW<3Q-7B&H( gA{Exn^#U%8n7Ce*Uk{}+Js>KJSPGd3_ZumeT@v(>Tu7h(8s z2x2p_|99@?2eLrm027dCXa+GiyE9s`XxBoW0XCA8frZ;W$kCG#Y5`D=5vUTvtC_rp zRa&wJD#i*F|Ib^;QCkC+Wv|=(fK`-{v2OA=wrC(3#110Yu}90*v4E^(U