Skip to content

Commit 7f2c82d

Browse files
authored
Merge pull request #551 from boriel/refact/remove_kind_constants
Refact/remove kind constants
2 parents d91e9cc + 98a19fe commit 7f2c82d

18 files changed

Lines changed: 174 additions & 209 deletions

src/api/__init__.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +0,0 @@
1-
#!/usr/bin/env python
2-
# -*- coding: utf-8 -*-
3-
# vim: ts=4:et:sw=4:
4-
5-
# ----------------------------------------------------------------------
6-
# Copyleft (K), Jose M. Rodriguez-Rosa (a.k.a. Boriel)
7-
#
8-
# This program is Free Software and is released under the terms of
9-
# the GNU General License
10-
# ----------------------------------------------------------------------
11-
12-
from src.api import debug # noqa
13-
from src.api import errors # noqa
14-
from src.api import errmsg # noqa
15-
from src.api import utils # noqa
16-
from src.api import symboltable # noqa

src/api/check.py

Lines changed: 49 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,34 @@
1111

1212
from typing import Union
1313

14-
import src.api.errmsg as errmsg
14+
from src.api import config
15+
from src.api import global_
16+
from src.api import errmsg
17+
1518
import src.symbols as symbols
1619

1720
from src.symbols.type_ import Type
18-
19-
from .constants import CLASS
20-
from .constants import SCOPE
21-
22-
from . import config
23-
from . import global_
24-
25-
__all__ = ['check_type',
26-
'check_is_declared_explicit',
27-
'check_and_make_label',
28-
'check_type_is_explicit',
29-
'check_call_arguments',
30-
'check_pending_calls',
31-
'check_pending_labels',
32-
'is_number',
33-
'is_const',
34-
'is_static',
35-
'is_string',
36-
'is_numeric',
37-
'is_dynamic',
38-
'is_null',
39-
'is_unsigned',
40-
'common_type'
41-
]
21+
from src.api.constants import CLASS, SCOPE
22+
23+
24+
__all__ = [
25+
'check_type',
26+
'check_is_declared_explicit',
27+
'check_and_make_label',
28+
'check_type_is_explicit',
29+
'check_call_arguments',
30+
'check_pending_calls',
31+
'check_pending_labels',
32+
'is_number',
33+
'is_const',
34+
'is_static',
35+
'is_string',
36+
'is_numeric',
37+
'is_dynamic',
38+
'is_null',
39+
'is_unsigned',
40+
'common_type'
41+
]
4242

4343

4444
# ----------------------------------------------------------------------
@@ -80,6 +80,18 @@ def check_is_declared_explicit(lineno: int, id_: str, classname: str = 'variable
8080
return global_.SYMBOL_TABLE.check_is_declared(id_, lineno, classname)
8181

8282

83+
def check_is_callable(lineno: int, id_: str) -> bool:
84+
entry = global_.SYMBOL_TABLE.get_entry(id_)
85+
if entry is None:
86+
return False
87+
88+
if entry.class_ not in (CLASS.function, CLASS.sub):
89+
errmsg.syntax_error_unexpected_class(lineno, id_, entry.class_, CLASS.function)
90+
return False
91+
92+
return True
93+
94+
8395
def check_type_is_explicit(lineno: int, id_: str, type_):
8496
assert isinstance(type_, symbols.TYPE)
8597
if type_.implicit:
@@ -96,7 +108,7 @@ def check_call_arguments(lineno: int, id_: str, args):
96108
if not global_.SYMBOL_TABLE.check_is_declared(id_, lineno, 'function'):
97109
return False
98110

99-
if not global_.SYMBOL_TABLE.check_class(id_, CLASS.function, lineno):
111+
if not check_is_callable(lineno, id_):
100112
return False
101113

102114
entry = global_.SYMBOL_TABLE.get_entry(id_)
@@ -428,3 +440,14 @@ def is_ender(node) -> bool:
428440
'CONTINUE_DO', 'CONTINUE_FOR', 'CONTINUE_WHILE',
429441
'EXIT_DO', 'EXIT_FOR', 'EXIT_WHILE',
430442
'GOTO', 'RETURN', 'STOP'}
443+
444+
445+
def check_class(node, class_: CLASS, lineno: int) -> bool:
446+
""" Returns whether the given node has CLASS.unknown or the given class_.
447+
It False, it will emit a syntax error
448+
"""
449+
if node.class_ == CLASS.unknown or node.class_ == class_:
450+
return True
451+
452+
errmsg.syntax_error_unexpected_class(lineno, node.name, node.class_, class_)
453+
return False

src/api/constants.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -187,26 +187,6 @@ def to_string(scope: 'SCOPE'):
187187
return scope.value
188188

189189

190-
@enum.unique
191-
class KIND(str, enum.Enum):
192-
""" Enum kind
193-
"""
194-
unknown = 'unknown'
195-
var = 'var'
196-
function = 'function'
197-
sub = 'sub'
198-
type = 'type'
199-
200-
@staticmethod
201-
def is_valid(kind: Union[str, 'KIND']):
202-
return kind in set(KIND)
203-
204-
@staticmethod
205-
def to_string(kind: 'KIND'):
206-
assert KIND.is_valid(kind)
207-
return kind.value
208-
209-
210190
@enum.unique
211191
class CONVENTION(str, enum.Enum):
212192
unknown = 'unknown'

src/api/errmsg.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
from typing import Callable
1616
from typing import Optional
1717

18-
from . import global_
19-
from .config import OPTIONS
18+
from src.api import global_
19+
20+
from src.api.constants import CLASS
21+
from src.api.config import OPTIONS
2022

2123

2224
# Exports only these functions. Others
@@ -125,7 +127,7 @@ def warning_implicit_type(lineno: int, id_: str, type_: str = None):
125127
return
126128

127129
if type_ is None:
128-
type_ = global_.DEFAULT_TYPE
130+
type_ = global_.DEFAULT_TYPE.name
129131

130132
warning(lineno, "Using default implicit type '%s' for '%s'" % (type_, id_))
131133

@@ -315,4 +317,19 @@ def syntax_error_cannot_initialize_array_of_type(lineno: int, type_name: str):
315317
error(lineno, f"Cannot initialize array of type {type_name}")
316318

317319

320+
# ----------------------------------------
321+
# Error, ID is a ... not a ...
322+
# ----------------------------------------
323+
def syntax_error_unexpected_class(lineno: int, id_name: str, wrong_class: CLASS, good_class: CLASS):
324+
n1 = 'n' if wrong_class[0] in 'aeiou' else ''
325+
n2 = 'n' if good_class[0] in 'aeiou' else ''
326+
error(lineno, f"'{id_name}' is a{n1} {wrong_class.upper()}, not a{n2} {good_class.upper()}")
327+
328+
329+
# ----------------------------------------
330+
# ID already declared as <class> at <line>
331+
# ----------------------------------------
332+
def syntax_error_already_declared(lineno: int, id_name: str, as_class: CLASS, at_lineno: int):
333+
error(lineno, f"'{id_name}' already declared as {as_class} at {at_lineno}")
334+
318335
# endregion

src/api/optimize.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from src.ast import NodeVisitor
1515
from src.api import errmsg
1616

17-
from src.api.constants import TYPE, SCOPE, CLASS, KIND, CONVENTION
17+
from src.api.constants import TYPE, SCOPE, CLASS, CONVENTION
1818
from src.api.debug import __DEBUG__
1919
from src.api.errmsg import warning_not_used
2020

@@ -87,8 +87,8 @@ def _visit(self, node: ToVisit):
8787
class UnreachableCodeVisitor(UniqueVisitor):
8888
""" Visitor to optimize unreachable code (and prune it).
8989
"""
90-
def visit_FUNCTION(self, node):
91-
if node.kind == KIND.function and node.body.token == 'BLOCK' and \
90+
def visit_FUNCTION(self, node: symbols.FUNCTION):
91+
if node.class_ == CLASS.function and node.body.token == 'BLOCK' and \
9292
(not node.body or node.body[-1].token != 'RETURN'):
9393
# String functions must *ALWAYS* return a value.
9494
# Put a sentinel ("dummy") return "" sentence that will be removed if other is detected

src/api/symboltable.py

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616
from typing import Optional
1717
from typing import Union
1818

19+
from src.api import errmsg
1920
import src.api.check as check
2021

21-
from .debug import __DEBUG__
22-
from . import global_
22+
from src.api.debug import __DEBUG__
23+
from src.api import global_
2324

2425
from src import symbols
2526
from src.symbols.symbol_ import Symbol
@@ -254,7 +255,7 @@ def check_is_undeclared(self, id_: str, lineno: int, classname='identifier',
254255
scope[id_].lineno))
255256
return False
256257

257-
def check_class(self, id_: str, class_, lineno: int, scope=None, show_error=True) -> bool:
258+
def check_class(self, id_: str, class_: CLASS, lineno: int, scope: Scope = None, show_error=True) -> bool:
258259
""" Check the id is either undefined or defined with
259260
the given class.
260261
@@ -268,24 +269,13 @@ def check_class(self, id_: str, class_, lineno: int, scope=None, show_error=True
268269
"""
269270
assert CLASS.is_valid(class_)
270271
entry = self.get_entry(id_, scope)
271-
if entry is None or entry.class_ == CLASS.unknown: # Undeclared yet
272+
if entry is None or entry.class_ in (CLASS.unknown, class_): # Undeclared yet
272273
return True
273274

274-
if entry.class_ != class_:
275-
if show_error:
276-
if entry.class_ == CLASS.array:
277-
a1 = 'n'
278-
else:
279-
a1 = ''
280-
if class_ == CLASS.array:
281-
a2 = 'n'
282-
else:
283-
a2 = ''
284-
syntax_error(lineno, "identifier '%s' is a%s %s, not a%s %s" %
285-
(id_, a1, entry.class_, a2, class_))
286-
return False
275+
if show_error:
276+
check.check_class(entry, class_, lineno)
287277

288-
return True
278+
return False
289279

290280
# -------------------------------------------------------------------------
291281
# Scope Management
@@ -354,7 +344,7 @@ def leave_scope(self, show_warnings=True):
354344
warning_not_used(v.lineno, v.name, kind=kind)
355345

356346
for entry in self.current_scope.values(filter_by_opt=True): # Symbols of the current level
357-
if entry.class_ is CLASS.unknown:
347+
if entry.class_ == CLASS.unknown:
358348
self.move_to_global_scope(entry.name)
359349

360350
offset = self.compute_offsets(self.current_scope)
@@ -428,9 +418,8 @@ def access_id(self, id_: str, lineno: int, scope=None, default_type=None, defaul
428418
default_type = symbols.TYPEREF(self.basic_types[global_.DEFAULT_IMPLICIT_TYPE],
429419
lineno, implicit=True)
430420

431-
result = self.declare_variable(id_, lineno, default_type)
421+
result = self.declare_variable(id_, lineno, default_type, class_=default_class)
432422
result.declared = False # It was implicitly declared
433-
result.class_ = default_class
434423
return result
435424

436425
# The entry was already declared. If it's type is auto and the default type is not None,
@@ -498,9 +487,10 @@ def access_func(self, id_: str, lineno: int, scope=None, default_type=None):
498487
else:
499488
default_type = symbols.TYPEREF(self.basic_types[global_.DEFAULT_TYPE], lineno, implicit=True)
500489

501-
return self.declare_func(id_, lineno, default_type)
490+
return self.declare_func(id_, lineno, default_type, class_=CLASS.unknown) # Declare SUB / Func
502491

503-
if not self.check_class(id_, CLASS.function, lineno, scope):
492+
if result.class_ not in (CLASS.function, CLASS.sub, CLASS.unknown):
493+
errmsg.syntax_error_unexpected_class(lineno, id_, result.class_, CLASS.function)
504494
return None
505495

506496
return result
@@ -549,7 +539,7 @@ def access_label(self, id_: str, lineno: int, scope: Optional[Scope] = None):
549539

550540
return result
551541

552-
def declare_variable(self, id_, lineno, type_, default_value=None):
542+
def declare_variable(self, id_, lineno, type_, default_value=None, class_: CLASS = CLASS.var):
553543
""" Like the above, but checks that entry.declared is False.
554544
Otherwise raises an error.
555545
@@ -567,11 +557,11 @@ def declare_variable(self, id_, lineno, type_, default_value=None):
567557
"%s:%i" % (id_, entry.filename, entry.lineno))
568558
return None
569559

570-
if not self.check_class(id_, CLASS.var, lineno, scope=self.current_scope):
560+
if not self.check_class(id_, class_, lineno, scope=self.current_scope):
571561
return None
572562

573563
entry = (self.get_entry(id_, scope=self.current_scope) or
574-
self.declare(id_, lineno, symbols.VAR(id_, lineno, class_=CLASS.var)))
564+
self.declare(id_, lineno, symbols.VAR(id_, lineno, class_=class_)))
575565
__DEBUG__("Entry %s declared with class %s at scope %s" % (entry.name, CLASS.to_string(entry.class_),
576566
self.current_scope.namespace))
577567

@@ -588,7 +578,7 @@ def declare_variable(self, id_, lineno, type_, default_value=None):
588578

589579
entry.scope = SCOPE.global_ if self.current_scope == self.global_scope else SCOPE.local
590580
entry.callable = False
591-
entry.class_ = CLASS.var # HINT: class_ attribute could be erased if access_id was used.
581+
entry.class_ = class_ # Ensure class_ is set if variable was implicit
592582
entry.declared = True # marks it as declared
593583

594584
if entry.type_.implicit and entry.type_ != self.basic_types[TYPE.unknown]:
@@ -644,11 +634,10 @@ def declare_const(self, id_: str, lineno: int, type_, default_value):
644634
"%s:%i" % (id_, entry.filename, entry.lineno))
645635
return None
646636

647-
entry = self.declare_variable(id_, lineno, type_, default_value)
637+
entry = self.declare_variable(id_, lineno, type_, default_value, class_=CLASS.const)
648638
if entry is None:
649639
return None
650640

651-
entry.class_ = CLASS.const
652641
return entry
653642

654643
def declare_label(self, id_: str, lineno: int) -> Optional[SymbolLABEL]:
@@ -784,15 +773,13 @@ def declare_array(self, id_: str, lineno: int, type_, bounds, default_value=None
784773
self.current_scope))
785774
return entry
786775

787-
def declare_func(self, id_: str, lineno: int, type_=None):
776+
def declare_func(self, id_: str, lineno: int, type_=None, class_=CLASS.function):
788777
""" Declares a function in the current scope.
789778
Checks whether the id exist or not (error if exists).
790779
And creates the entry at the symbol table.
791780
"""
792-
if not self.check_class(id_, 'function', lineno):
793-
entry = self.get_entry(id_) # Must not exist or have _class = None or Function and declared = False
794-
an = 'an' if entry.class_.lower()[0] in 'aeio' else 'a'
795-
syntax_error(lineno, "'%s' already declared as %s %s at %i" % (id_, an, entry.class_, entry.lineno))
781+
assert class_ in (CLASS.function, CLASS.sub, CLASS.unknown)
782+
if not self.check_class(id_, class_, lineno):
796783
return None
797784

798785
entry = self.get_entry(id_) # Must not exist or have _class = None or Function and declared = False
@@ -801,18 +788,15 @@ def declare_func(self, id_: str, lineno: int, type_=None):
801788
syntax_error(lineno, "Duplicate function name '%s', previously defined at %i" % (id_, entry.lineno))
802789
return None
803790

804-
if entry.class_ != CLASS.unknown and entry.callable is False: # HINT: Must use is False here.
805-
syntax_error_not_array_nor_func(lineno, id_)
806-
return None
807-
808791
if id_[-1] in DEPRECATED_SUFFIXES and entry.type_ != self.basic_types[SUFFIX_TYPE[id_[-1]]]:
809792
syntax_error_func_type_mismatch(lineno, entry)
810793

811794
if entry.token == 'VAR': # This was a function used in advance
812795
symbols.VAR.to_function(entry, lineno=lineno)
813796
entry.mangled = '%s_%s' % (self.current_namespace, entry.name) # HINT: mangle for nexted scopes
797+
entry.class_ = class_
814798
else:
815-
entry = self.declare(id_, lineno, symbols.FUNCTION(id_, lineno, type_=type_))
799+
entry = self.declare(id_, lineno, symbols.FUNCTION(id_, lineno, type_=type_, class_=class_))
816800

817801
if entry.forwarded:
818802
entry.forwared = False # No longer forwarded

0 commit comments

Comments
 (0)