Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add: 'const' keyword to allow users to define new constants #302

Merged
merged 4 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions nml/ast/constant.py
Original file line number Diff line number Diff line change
@@ -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)
17 changes: 8 additions & 9 deletions nml/ast/disable_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
10 changes: 6 additions & 4 deletions nml/ast/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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)
Expand Down
8 changes: 7 additions & 1 deletion nml/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
basecost,
cargotable,
conditional,
constant,
deactivate,
disable_item,
error,
Expand Down Expand Up @@ -153,7 +154,8 @@ def p_main_block(self, t):
| snowline
| engine_override
| sort_vehicles
| basecost"""
| basecost
| constant"""
t[0] = t[1]

#
Expand Down Expand Up @@ -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])
1 change: 1 addition & 0 deletions nml/tokens.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"recolour_sprite": "RECOLOUR_SPRITE",
"engine_override": "ENGINE_OVERRIDE",
"sort": "SORT_VEHICLES",
"const": "CONST"
}
# fmt: on

Expand Down
4 changes: 2 additions & 2 deletions regression/030_house.nml
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Binary file modified regression/expected/030_house.grf
Binary file not shown.
Loading