Skip to content

Commit 7c6e1dd

Browse files
authored
Merge pull request #599 from boriel/refact/symbol_table
refact: split symboltable into a Python pkg
2 parents 3dd74af + 0b3d497 commit 7c6e1dd

10 files changed

Lines changed: 107 additions & 102 deletions

File tree

src/api/global_.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
# the GNU General License
1010
# ----------------------------------------------------------------------
1111
from typing import Dict, List, NamedTuple, Optional, Set
12-
13-
import src.api
14-
1512
from src.api.opcodestemps import OpcodesTemps
1613
from src.api.constants import TYPE, LoopType
1714

@@ -82,7 +79,7 @@ class LoopInfo(NamedTuple):
8279
# ----------------------------------------------------------------------
8380
# Global Symbol Table
8481
# ----------------------------------------------------------------------
85-
SYMBOL_TABLE: Optional["src.api.symboltable.SymbolTable"] = None # Must be initialized with SymbolTable()
82+
SYMBOL_TABLE = None # Must be initialized with SymbolTable instance
8683

8784
# ----------------------------------------------------------------------
8885
# Function calls pending to check

src/api/optimize.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import Set
77

88
import src.api.global_ as gl
9+
import src.api.symboltable.symboltable
910
import src.api.utils
1011
import src.api.symboltable
1112
import src.api.check as chk
@@ -478,7 +479,9 @@ def _update_bound_status(self, arg: symbols.VARARRAY):
478479
return
479480

480481
if arg.scope == SCOPE.local and not arg.byref:
481-
arg.scopeRef.owner.locals_size = src.api.symboltable.SymbolTable.compute_offsets(arg.scopeRef)
482+
arg.scopeRef.owner.locals_size = src.api.symboltable.symboltable.SymbolTable.compute_offsets(
483+
arg.scopeRef
484+
)
482485

483486

484487
class VarDependency(NamedTuple):

src/api/symboltable/__init__.py

Whitespace-only changes.

src/api/symboltable/scope.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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 collections import OrderedDict
13+
from typing import Optional, Dict
14+
15+
from src.api.config import OPTIONS
16+
from src.symbols.symbol_ import Symbol
17+
from src.symbols.var import SymbolVAR
18+
19+
20+
class Scope:
21+
"""Implements a Scope in the SymbolTable
22+
23+
A Scope is just a dictionary.
24+
25+
To get a symbol, just access it by it's name. So scope['a'] will
26+
return the 'a' symbol (e.g. a declared variable 'a') or None
27+
if nothing is declared in that scope (no KeyError exception is raised
28+
if the identifier is not defined in such scope).
29+
30+
The caseins dict stores the symbol names in lowercase only if
31+
the global OPTION ignore case is enabled (True). This is because
32+
most BASIC dialects are case insensitive. 'caseins' will be used
33+
as a fallback if the symbol name does not exists.
34+
35+
On init() the parent mangle can be stored. The mangle is a prefix
36+
added to every symbol to avoid name collision.
37+
38+
E.g. for a global var o function, the mangle will be '_'. So
39+
'a' will be output in asm as '_a'. For nested scopes, the mangled
40+
is composed as _function-name_varname. So a local variable in function
41+
myFunct will be output as _myFunct_a.
42+
"""
43+
44+
def __init__(self, namespace: str = "", parent_scope: Optional["Scope"] = None):
45+
self.symbols: Dict[str, SymbolVAR] = OrderedDict()
46+
self.caseins: Dict[str, SymbolVAR] = OrderedDict()
47+
self.namespace: str = namespace
48+
self.owner: Optional[SymbolVAR] = None # Function, Sub, etc. owning this scope
49+
self.parent_scope: Optional["Scope"] = parent_scope
50+
self.parent_namespace: Optional[str] = parent_scope.namespace if parent_scope is not None else None
51+
52+
def __getitem__(self, key: str) -> Optional[SymbolVAR]:
53+
return self.symbols.get(key, self.caseins.get(key.lower(), None))
54+
55+
def __setitem__(self, key: str, value: SymbolVAR):
56+
assert isinstance(value, Symbol)
57+
self.symbols[key] = value
58+
if value.caseins: # Declared with case insensitive option?
59+
self.caseins[key.lower()] = value
60+
61+
def __delitem__(self, key: str):
62+
self.symbols.pop(key, None)
63+
self.caseins.pop(key, None)
64+
65+
def values(self, filter_by_opt=True):
66+
if filter_by_opt and OPTIONS.optimization_level > 1:
67+
return [y for x, y in self.symbols.items() if y.accessed]
68+
return [y for x, y in self.symbols.items()]
69+
70+
def keys(self, filter_by_opt=True):
71+
if filter_by_opt and OPTIONS.optimization_level > 1:
72+
return [x for x, y in self.symbols.items() if y.accessed]
73+
return self.symbols.keys()
74+
75+
def items(self, filter_by_opt=True):
76+
if filter_by_opt and OPTIONS.optimization_level > 1:
77+
return [(x, y) for x, y in self.symbols.items() if y.accessed]
78+
return self.symbols.items()
Lines changed: 15 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -9,101 +9,27 @@
99
# the GNU General License
1010
# ----------------------------------------------------------------------
1111

12-
from collections import OrderedDict
13-
14-
from typing import Dict
15-
from typing import List
16-
from typing import Optional
17-
from typing import Union
18-
19-
from src.api import errmsg
20-
import src.api.check as check
21-
22-
from src.api.debug import __DEBUG__
23-
from src.api import global_
12+
from typing import List, Dict, Optional, Union
2413

2514
from src import symbols
15+
from src.api import global_, check as check, errmsg
16+
from src.api.config import OPTIONS
17+
from src.api.constants import TYPE, DEPRECATED_SUFFIXES, SUFFIX_TYPE, CLASS, SCOPE
18+
from src.api.debug import __DEBUG__
19+
from src.api.errmsg import (
20+
error as syntax_error,
21+
warning_not_used,
22+
warning_implicit_type,
23+
syntax_error_not_array_nor_func,
24+
syntax_error_cannot_define_default_array_argument,
25+
syntax_error_func_type_mismatch,
26+
)
27+
from src.api.symboltable.scope import Scope
28+
from src.symbols.label import SymbolLABEL
2629
from src.symbols.symbol_ import Symbol
2730
from src.symbols.var import SymbolVAR
28-
from src.symbols.label import SymbolLABEL
2931
from src.symbols.vararray import SymbolVARARRAY
3032

31-
from .config import OPTIONS
32-
33-
from .errmsg import error as syntax_error
34-
from .errmsg import warning_implicit_type
35-
from .errmsg import warning_not_used
36-
from .errmsg import syntax_error_func_type_mismatch
37-
from .errmsg import syntax_error_not_array_nor_func
38-
from .errmsg import syntax_error_cannot_define_default_array_argument
39-
40-
from .constants import DEPRECATED_SUFFIXES
41-
from .constants import SUFFIX_TYPE
42-
from .constants import SCOPE
43-
from .constants import CLASS
44-
from .constants import TYPE
45-
46-
47-
class Scope:
48-
"""Implements an Scope in the SymbolTable
49-
50-
An Scope is just a dictionary.
51-
52-
To get a symbol, just access it by it's name. So scope['a'] will
53-
return the 'a' symbol (e.g. a declared variable 'a') or None
54-
if nothing is declared in that scope (no KeyError exception is raised
55-
if the identifier is not defined in such scope).
56-
57-
The caseins dict stores the symbol names in lowercase only if
58-
the global OPTION ignore case is enabled (True). This is because
59-
most BASIC dialects are case insensitive. 'caseins' will be used
60-
as a fallback if the symbol name does not exists.
61-
62-
On init() the parent mangle can be stored. The mangle is a prefix
63-
added to every symbol to avoid name collision.
64-
65-
E.g. for a global var o function, the mangle will be '_'. So
66-
'a' will be output in asm as '_a'. For nested scopes, the mangled
67-
is composed as _function-name_varname. So a local variable in function
68-
myFunct will be output as _myFunct_a.
69-
"""
70-
71-
def __init__(self, namespace: str = "", parent_scope: Optional["Scope"] = None):
72-
self.symbols: Dict[str, SymbolVAR] = OrderedDict()
73-
self.caseins: Dict[str, SymbolVAR] = OrderedDict()
74-
self.namespace: str = namespace
75-
self.owner: Optional[SymbolVAR] = None # Function, Sub, etc. owning this scope
76-
self.parent_scope: Optional["Scope"] = parent_scope
77-
self.parent_namespace: Optional[str] = parent_scope.namespace if parent_scope is not None else None
78-
79-
def __getitem__(self, key: str) -> Optional[SymbolVAR]:
80-
return self.symbols.get(key, self.caseins.get(key.lower(), None))
81-
82-
def __setitem__(self, key: str, value: SymbolVAR):
83-
assert isinstance(value, Symbol)
84-
self.symbols[key] = value
85-
if value.caseins: # Declared with case insensitive option?
86-
self.caseins[key.lower()] = value
87-
88-
def __delitem__(self, key: str):
89-
self.symbols.pop(key, None)
90-
self.caseins.pop(key, None)
91-
92-
def values(self, filter_by_opt=True):
93-
if filter_by_opt and OPTIONS.optimization_level > 1:
94-
return [y for x, y in self.symbols.items() if y.accessed]
95-
return [y for x, y in self.symbols.items()]
96-
97-
def keys(self, filter_by_opt=True):
98-
if filter_by_opt and OPTIONS.optimization_level > 1:
99-
return [x for x, y in self.symbols.items() if y.accessed]
100-
return self.symbols.keys()
101-
102-
def items(self, filter_by_opt=True):
103-
if filter_by_opt and OPTIONS.optimization_level > 1:
104-
return [(x, y) for x, y in self.symbols.items() if y.accessed]
105-
return self.symbols.items()
106-
10733

10834
class SymbolTable:
10935
"""Implements a symbol table.

src/symbols/funcdecl.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@
88
# This program is Free Software and is released under the terms of
99
# the GNU General License
1010
# ----------------------------------------------------------------------
11-
11+
import src.api.symboltable.scope
1212
from src.api import global_
13-
from src.api import symboltable
1413
from src.api.constants import CLASS
1514

1615
from src.symbols.symbol_ import Symbol
@@ -52,7 +51,7 @@ def local_symbol_table(self):
5251

5352
@local_symbol_table.setter
5453
def local_symbol_table(self, value):
55-
assert isinstance(value, symboltable.Scope)
54+
assert isinstance(value, src.api.symboltable.scope.Scope)
5655
self.entry.local_symbol_table = value
5756

5857
@property

src/zxbc/zxbparser.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from typing import Optional
2626

2727
# Compiler API
28+
import src.api.symboltable.symboltable
2829
from src.api.debug import __DEBUG__ # analysis:ignore
2930
from src.api.opcodestemps import OpcodesTemps
3031
from src.api.errmsg import error
@@ -93,7 +94,7 @@
9394
# ----------------------------------------------------------------------
9495
# Global Symbol Table
9596
# ----------------------------------------------------------------------
96-
SYMBOL_TABLE = gl.SYMBOL_TABLE = src.api.symboltable.SymbolTable()
97+
SYMBOL_TABLE = gl.SYMBOL_TABLE = src.api.symboltable.symboltable.SymbolTable()
9798

9899
# ----------------------------------------------------------------------
99100
# Defined user labels. They all are prepended _label_. Line numbers 10,
@@ -155,7 +156,7 @@ def init():
155156
del gl.FUNCTION_CALLS[:]
156157
del gl.FUNCTION_LEVEL[:]
157158
del gl.FUNCTIONS[:]
158-
SYMBOL_TABLE = gl.SYMBOL_TABLE = src.api.symboltable.SymbolTable()
159+
SYMBOL_TABLE = gl.SYMBOL_TABLE = src.api.symboltable.symboltable.SymbolTable()
159160
OPTIONS = src.api.config.OPTIONS
160161

161162
# DATAs info

tests/api/test_symbolTable.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from unittest import TestCase
66
from io import StringIO
77

8-
from src.api.symboltable import SymbolTable
8+
from src.api.symboltable.symboltable import SymbolTable
99
from src.api.constants import TYPE
1010
from src.api.constants import SCOPE
1111
from src.api.constants import CLASS

tests/symbols/test_symbolARRAYACCESS.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from src import symbols
88
import src.api.global_ as gl
99
import src.api.config as config
10-
from src.api.symboltable import SymbolTable
10+
from src.api.symboltable.symboltable import SymbolTable
1111
from src.symbols.type_ import Type
1212
from src.zxbpp import zxbpp
1313

tests/symbols/test_symbolFUNCDECL.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from unittest import TestCase
66
import src.api.global_ as gl
77
import src.api.symboltable
8+
import src.api.symboltable.symboltable
89
from src.api.constants import CLASS
910

1011
from src.symbols import FUNCDECL
@@ -13,7 +14,7 @@
1314

1415
class TestSymbolFUNCDECL(TestCase):
1516
def setUp(self):
16-
src.api.global_.SYMBOL_TABLE = src.api.symboltable.SymbolTable()
17+
src.api.global_.SYMBOL_TABLE = src.api.symboltable.symboltable.SymbolTable()
1718
self.f = gl.SYMBOL_TABLE.declare_func("f", 1, type_=Type.ubyte)
1819
self.s = FUNCDECL(self.f, 1)
1920

0 commit comments

Comments
 (0)