multilingualprogramming 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. multilingualprogramming/__init__.py +74 -0
  2. multilingualprogramming/__main__.py +194 -0
  3. multilingualprogramming/codegen/__init__.py +12 -0
  4. multilingualprogramming/codegen/executor.py +215 -0
  5. multilingualprogramming/codegen/python_generator.py +592 -0
  6. multilingualprogramming/codegen/repl.py +489 -0
  7. multilingualprogramming/codegen/runtime_builtins.py +308 -0
  8. multilingualprogramming/core/__init__.py +12 -0
  9. multilingualprogramming/core/ir.py +29 -0
  10. multilingualprogramming/core/lowering.py +24 -0
  11. multilingualprogramming/datetime/__init__.py +11 -0
  12. multilingualprogramming/datetime/date_parser.py +190 -0
  13. multilingualprogramming/datetime/mp_date.py +210 -0
  14. multilingualprogramming/datetime/mp_datetime.py +153 -0
  15. multilingualprogramming/datetime/mp_time.py +147 -0
  16. multilingualprogramming/datetime/resource_loader.py +18 -0
  17. multilingualprogramming/exceptions.py +158 -0
  18. multilingualprogramming/imports.py +150 -0
  19. multilingualprogramming/keyword/__init__.py +13 -0
  20. multilingualprogramming/keyword/keyword_registry.py +249 -0
  21. multilingualprogramming/keyword/keyword_validator.py +59 -0
  22. multilingualprogramming/keyword/language_pack_validator.py +110 -0
  23. multilingualprogramming/lexer/__init__.py +11 -0
  24. multilingualprogramming/lexer/lexer.py +570 -0
  25. multilingualprogramming/lexer/source_reader.py +91 -0
  26. multilingualprogramming/lexer/token.py +54 -0
  27. multilingualprogramming/lexer/token_types.py +38 -0
  28. multilingualprogramming/numeral/__init__.py +11 -0
  29. multilingualprogramming/numeral/abstract_numeral.py +232 -0
  30. multilingualprogramming/numeral/complex_numeral.py +190 -0
  31. multilingualprogramming/numeral/fraction_numeral.py +165 -0
  32. multilingualprogramming/numeral/mp_numeral.py +243 -0
  33. multilingualprogramming/numeral/numeral_converter.py +151 -0
  34. multilingualprogramming/numeral/roman_numeral.py +301 -0
  35. multilingualprogramming/numeral/unicode_numeral.py +292 -0
  36. multilingualprogramming/parser/__init__.py +28 -0
  37. multilingualprogramming/parser/ast_nodes.py +459 -0
  38. multilingualprogramming/parser/ast_printer.py +677 -0
  39. multilingualprogramming/parser/error_messages.py +75 -0
  40. multilingualprogramming/parser/parser.py +1796 -0
  41. multilingualprogramming/parser/semantic_analyzer.py +689 -0
  42. multilingualprogramming/parser/surface_normalizer.py +282 -0
  43. multilingualprogramming/resources/datetime/eras.json +23 -0
  44. multilingualprogramming/resources/datetime/formats.json +32 -0
  45. multilingualprogramming/resources/datetime/months.json +150 -0
  46. multilingualprogramming/resources/datetime/weekdays.json +90 -0
  47. multilingualprogramming/resources/parser/error_messages.json +310 -0
  48. multilingualprogramming/resources/repl/commands.json +636 -0
  49. multilingualprogramming/resources/usm/builtins_aliases.json +731 -0
  50. multilingualprogramming/resources/usm/keywords.json +1063 -0
  51. multilingualprogramming/resources/usm/operators.json +532 -0
  52. multilingualprogramming/resources/usm/schema.json +34 -0
  53. multilingualprogramming/resources/usm/surface_patterns.json +1523 -0
  54. multilingualprogramming/unicode_string.py +140 -0
  55. multilingualprogramming/version.py +9 -0
  56. multilingualprogramming-0.2.0.dist-info/METADATA +350 -0
  57. multilingualprogramming-0.2.0.dist-info/RECORD +61 -0
  58. multilingualprogramming-0.2.0.dist-info/WHEEL +5 -0
  59. multilingualprogramming-0.2.0.dist-info/entry_points.txt +3 -0
  60. multilingualprogramming-0.2.0.dist-info/licenses/LICENSE +674 -0
  61. multilingualprogramming-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,210 @@
1
+ #
2
+ # SPDX-FileCopyrightText: 2024 John Samuel <johnsamuelwrites@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: GPL-3.0-or-later
5
+ #
6
+
7
+ """Multilingual date handling."""
8
+ # pylint: disable=unsubscriptable-object
9
+
10
+ import datetime as dt
11
+ from typing import Any, cast
12
+ from multilingualprogramming.datetime.date_parser import DateParser
13
+ from multilingualprogramming.datetime.resource_loader import load_datetime_resource
14
+ from multilingualprogramming.exceptions import InvalidDateError
15
+ from multilingualprogramming.unicode_string import get_unicode_character_string
16
+
17
+
18
+ class MPDate:
19
+ """
20
+ A date object with multilingual formatting and parsing.
21
+
22
+ Internally stores as Python datetime.date (Gregorian).
23
+ """
24
+
25
+ _months_data: dict[str, Any] | None = None
26
+
27
+ @classmethod
28
+ def _load_months(cls):
29
+ """Load month names from JSON."""
30
+ if cls._months_data is not None:
31
+ return
32
+ cls._months_data = load_datetime_resource("months.json")
33
+
34
+ def __init__(self, year=None, month=None, day=None, date=None):
35
+ """
36
+ Create an MPDate.
37
+
38
+ Parameters:
39
+ year (int): Year
40
+ month (int): Month (1-12)
41
+ day (int): Day (1-31)
42
+ date (datetime.date): Python date object
43
+ """
44
+ if date is not None:
45
+ self._date = date
46
+ elif year is not None and month is not None and day is not None:
47
+ try:
48
+ self._date = dt.date(year, month, day)
49
+ except ValueError as e:
50
+ raise InvalidDateError(str(e)) from e
51
+ else:
52
+ raise InvalidDateError(
53
+ "MPDate requires year/month/day or a date object"
54
+ )
55
+
56
+ @classmethod
57
+ def from_string(cls, datestr, language=None):
58
+ """
59
+ Parse a multilingual date string.
60
+
61
+ Parameters:
62
+ datestr (str): Date string in any supported language
63
+ language (str): Optional language hint
64
+
65
+ Returns:
66
+ MPDate: Parsed date
67
+ """
68
+ year, month, day, _ = DateParser.parse(datestr, language)
69
+ return cls(year=year, month=month, day=day)
70
+
71
+ def to_date(self):
72
+ """
73
+ Convert to Python datetime.date.
74
+
75
+ Returns:
76
+ datetime.date: Python date object
77
+ """
78
+ return self._date
79
+
80
+ @property
81
+ def year(self):
82
+ """Return the year."""
83
+ return self._date.year
84
+
85
+ @property
86
+ def month(self):
87
+ """Return the month."""
88
+ return self._date.month
89
+
90
+ @property
91
+ def day(self):
92
+ """Return the day."""
93
+ return self._date.day
94
+
95
+ def _get_month_name(self, language, abbreviated=False):
96
+ """Get the month name in a given language."""
97
+ self._load_months()
98
+ if self._months_data is None:
99
+ raise InvalidDateError("Month data not loaded")
100
+ months_data = cast(dict[str, Any], self._months_data)
101
+ month_keys = list(months_data["months"].keys())
102
+ month_key = month_keys[self._date.month - 1]
103
+ month_data = months_data["months"][month_key]
104
+ if language in month_data:
105
+ form = "abbr" if abbreviated else "full"
106
+ return month_data[language][form]
107
+ return month_data["en"]["full" if not abbreviated else "abbr"]
108
+
109
+ def _format_number(self, number, script=None):
110
+ """Format a number in the given script."""
111
+ if script and script not in ("en", "DIGIT"):
112
+ # Map language codes to Unicode script names
113
+ script_map = {
114
+ "hi": "DEVANAGARI",
115
+ "ar": "ARABIC-INDIC",
116
+ "bn": "BENGALI",
117
+ "ta": "TAMIL",
118
+ }
119
+ unicode_script = script_map.get(script)
120
+ if unicode_script:
121
+ return get_unicode_character_string(unicode_script, number)
122
+ return str(number)
123
+
124
+ def to_string(self, language="en", fmt=None):
125
+ """
126
+ Format date in a given language.
127
+
128
+ Parameters:
129
+ language (str): Language code (e.g., "en", "fr", "hi")
130
+ fmt (str): Optional format override
131
+
132
+ Returns:
133
+ str: Formatted date string
134
+ """
135
+ if fmt is not None:
136
+ return self._date.strftime(fmt)
137
+
138
+ month_name = self._get_month_name(language)
139
+ day_str = self._format_number(self._date.day, language)
140
+ year_str = self._format_number(self._date.year, language)
141
+
142
+ if language in ("zh", "ja"):
143
+ return f"{year_str}年{self._format_number(self._date.month, language)}月{day_str}日"
144
+
145
+ return f"{day_str}-{month_name}-{year_str}"
146
+
147
+ def __add__(self, days):
148
+ """
149
+ Add days to the date.
150
+
151
+ Parameters:
152
+ days (int): Number of days to add
153
+
154
+ Returns:
155
+ MPDate: New date
156
+ """
157
+ if isinstance(days, int):
158
+ new_date = self._date + dt.timedelta(days=days)
159
+ return MPDate(date=new_date)
160
+ return NotImplemented
161
+
162
+ def __sub__(self, other):
163
+ """
164
+ Subtract another MPDate or days.
165
+
166
+ Returns:
167
+ int: Difference in days (if MPDate)
168
+ MPDate: New date (if int)
169
+ """
170
+ if isinstance(other, MPDate):
171
+ delta = self._date - other._date
172
+ return delta.days
173
+ if isinstance(other, int):
174
+ new_date = self._date - dt.timedelta(days=other)
175
+ return MPDate(date=new_date)
176
+ return NotImplemented
177
+
178
+ def __eq__(self, other):
179
+ if isinstance(other, MPDate):
180
+ return self._date == other._date
181
+ return NotImplemented
182
+
183
+ def __lt__(self, other):
184
+ if isinstance(other, MPDate):
185
+ return self._date < other._date
186
+ return NotImplemented
187
+
188
+ def __le__(self, other):
189
+ if isinstance(other, MPDate):
190
+ return self._date <= other._date
191
+ return NotImplemented
192
+
193
+ def __gt__(self, other):
194
+ if isinstance(other, MPDate):
195
+ return self._date > other._date
196
+ return NotImplemented
197
+
198
+ def __ge__(self, other):
199
+ if isinstance(other, MPDate):
200
+ return self._date >= other._date
201
+ return NotImplemented
202
+
203
+ def __hash__(self):
204
+ return hash(self._date)
205
+
206
+ def __str__(self):
207
+ return self.to_string("en")
208
+
209
+ def __repr__(self):
210
+ return f"MPDate({self._date.year}, {self._date.month}, {self._date.day})"
@@ -0,0 +1,153 @@
1
+ #
2
+ # SPDX-FileCopyrightText: 2024 John Samuel <johnsamuelwrites@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: GPL-3.0-or-later
5
+ #
6
+
7
+ """Multilingual datetime handling."""
8
+
9
+ import datetime as dt
10
+ from multilingualprogramming.datetime.mp_date import MPDate
11
+ from multilingualprogramming.datetime.mp_time import MPTime
12
+ from multilingualprogramming.datetime.date_parser import DateParser
13
+ from multilingualprogramming.exceptions import InvalidDateError
14
+
15
+
16
+ class MPDatetime:
17
+ """
18
+ A datetime object with multilingual formatting and parsing.
19
+
20
+ Combines MPDate and MPTime. Internally stores as Python datetime.datetime.
21
+ """
22
+
23
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
24
+ def __init__(self, year=None, month=None, day=None,
25
+ hour=0, minute=0, second=0, datetime_obj=None):
26
+ """
27
+ Create an MPDatetime.
28
+
29
+ Parameters:
30
+ year (int): Year
31
+ month (int): Month (1-12)
32
+ day (int): Day (1-31)
33
+ hour (int): Hour (0-23)
34
+ minute (int): Minute (0-59)
35
+ second (int): Second (0-59)
36
+ datetime_obj (datetime.datetime): Python datetime object
37
+ """
38
+ if datetime_obj is not None:
39
+ self._datetime = datetime_obj
40
+ elif year is not None and month is not None and day is not None:
41
+ try:
42
+ self._datetime = dt.datetime(
43
+ year, month, day, hour, minute, second
44
+ )
45
+ except ValueError as e:
46
+ raise InvalidDateError(str(e)) from e
47
+ else:
48
+ raise InvalidDateError(
49
+ "MPDatetime requires year/month/day or a datetime object"
50
+ )
51
+
52
+ @classmethod
53
+ def from_string(cls, datestr, language=None):
54
+ """
55
+ Parse a multilingual date string into an MPDatetime.
56
+
57
+ Parameters:
58
+ datestr (str): Date string in any supported language
59
+ language (str): Optional language hint
60
+
61
+ Returns:
62
+ MPDatetime: Parsed datetime (time defaults to 00:00:00)
63
+ """
64
+ year, month, day, _ = DateParser.parse(datestr, language)
65
+ return cls(year=year, month=month, day=day)
66
+
67
+ @classmethod
68
+ def now(cls):
69
+ """Create an MPDatetime for the current date and time."""
70
+ return cls(datetime_obj=dt.datetime.now())
71
+
72
+ def to_datetime(self):
73
+ """Convert to Python datetime.datetime."""
74
+ return self._datetime
75
+
76
+ def date(self):
77
+ """Get the date part as MPDate."""
78
+ return MPDate(date=self._datetime.date())
79
+
80
+ def time(self):
81
+ """Get the time part as MPTime."""
82
+ return MPTime(time=self._datetime.time())
83
+
84
+ @property
85
+ def year(self):
86
+ """Return the year."""
87
+ return self._datetime.year
88
+
89
+ @property
90
+ def month(self):
91
+ """Return the month."""
92
+ return self._datetime.month
93
+
94
+ @property
95
+ def day(self):
96
+ """Return the day."""
97
+ return self._datetime.day
98
+
99
+ @property
100
+ def hour(self):
101
+ """Return the hour."""
102
+ return self._datetime.hour
103
+
104
+ @property
105
+ def minute(self):
106
+ """Return the minute."""
107
+ return self._datetime.minute
108
+
109
+ @property
110
+ def second(self):
111
+ """Return the second."""
112
+ return self._datetime.second
113
+
114
+ def to_string(self, language="en", fmt=None):
115
+ """
116
+ Format datetime in a given language.
117
+
118
+ Parameters:
119
+ language (str): Language code
120
+ fmt (str): Optional format override
121
+
122
+ Returns:
123
+ str: Formatted datetime string
124
+ """
125
+ if fmt is not None:
126
+ return self._datetime.strftime(fmt)
127
+
128
+ date_part = self.date().to_string(language)
129
+ time_part = self.time().to_string(language)
130
+ return f"{date_part} {time_part}"
131
+
132
+ def __eq__(self, other):
133
+ if isinstance(other, MPDatetime):
134
+ return self._datetime == other._datetime
135
+ return NotImplemented
136
+
137
+ def __lt__(self, other):
138
+ if isinstance(other, MPDatetime):
139
+ return self._datetime < other._datetime
140
+ return NotImplemented
141
+
142
+ def __hash__(self):
143
+ return hash(self._datetime)
144
+
145
+ def __str__(self):
146
+ return self.to_string("en")
147
+
148
+ def __repr__(self):
149
+ return (
150
+ f"MPDatetime({self._datetime.year}, {self._datetime.month}, "
151
+ f"{self._datetime.day}, {self._datetime.hour}, "
152
+ f"{self._datetime.minute}, {self._datetime.second})"
153
+ )
@@ -0,0 +1,147 @@
1
+ #
2
+ # SPDX-FileCopyrightText: 2024 John Samuel <johnsamuelwrites@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: GPL-3.0-or-later
5
+ #
6
+
7
+ """Multilingual time handling."""
8
+
9
+ import datetime as dt
10
+ from multilingualprogramming.exceptions import InvalidDateError
11
+ from multilingualprogramming.unicode_string import get_unicode_character_string
12
+
13
+
14
+ class MPTime:
15
+ """
16
+ A time object with multilingual formatting.
17
+
18
+ Internally stores as Python datetime.time.
19
+ """
20
+
21
+ # Script mapping for numeral conversion
22
+ SCRIPT_MAP = {
23
+ "hi": "DEVANAGARI",
24
+ "ar": "ARABIC-INDIC",
25
+ "bn": "BENGALI",
26
+ "ta": "TAMIL",
27
+ }
28
+
29
+ def __init__(self, hour=0, minute=0, second=0, time=None):
30
+ """
31
+ Create an MPTime.
32
+
33
+ Parameters:
34
+ hour (int): Hour (0-23)
35
+ minute (int): Minute (0-59)
36
+ second (int): Second (0-59)
37
+ time (datetime.time): Python time object
38
+ """
39
+ if time is not None:
40
+ self._time = time
41
+ else:
42
+ try:
43
+ self._time = dt.time(hour, minute, second)
44
+ except ValueError as e:
45
+ raise InvalidDateError(str(e)) from e
46
+
47
+ def to_time(self):
48
+ """Convert to Python datetime.time."""
49
+ return self._time
50
+
51
+ @property
52
+ def hour(self):
53
+ """Return the hour."""
54
+ return self._time.hour
55
+
56
+ @property
57
+ def minute(self):
58
+ """Return the minute."""
59
+ return self._time.minute
60
+
61
+ @property
62
+ def second(self):
63
+ """Return the second."""
64
+ return self._time.second
65
+
66
+ def _format_number(self, number, language, pad=True):
67
+ """Format a number in the given script, zero-padded to 2 digits."""
68
+ unicode_script = self.SCRIPT_MAP.get(language)
69
+ if unicode_script:
70
+ numstr = get_unicode_character_string(unicode_script, number)
71
+ if pad and number < 10:
72
+ zero = get_unicode_character_string(unicode_script, 0)
73
+ numstr = zero + numstr
74
+ return numstr
75
+ if pad:
76
+ return f"{number:02d}"
77
+ return str(number)
78
+
79
+ def to_string(self, language="en", use_24h=True):
80
+ """
81
+ Format time in a given language.
82
+
83
+ Parameters:
84
+ language (str): Language code
85
+ use_24h (bool): Use 24-hour format (default True)
86
+
87
+ Returns:
88
+ str: Formatted time string
89
+ """
90
+ if use_24h:
91
+ h = self._format_number(self._time.hour, language)
92
+ m = self._format_number(self._time.minute, language)
93
+ s = self._format_number(self._time.second, language)
94
+ return f"{h}:{m}:{s}"
95
+
96
+ # 12-hour format
97
+ hour_12 = self._time.hour % 12
98
+ if hour_12 == 0:
99
+ hour_12 = 12
100
+ is_pm = self._time.hour >= 12
101
+
102
+ h = self._format_number(hour_12, language)
103
+ m = self._format_number(self._time.minute, language)
104
+ s = self._format_number(self._time.second, language)
105
+ time_str = f"{h}:{m}:{s}"
106
+
107
+ am_pm_map = {
108
+ "en": ("AM", "PM"),
109
+ "fr": ("", ""),
110
+ "es": ("a.m.", "p.m."),
111
+ "de": ("", ""),
112
+ "hi": ("पूर्वाह्न", "अपराह्न"),
113
+ "ar": ("ص", "م"),
114
+ "bn": ("পূর্বাহ্ন", "অপরাহ্ন"),
115
+ "ta": ("முற்பகல்", "பிற்பகல்"),
116
+ "zh": ("上午", "下午"),
117
+ "ja": ("午前", "午後"),
118
+ }
119
+ am, pm = am_pm_map.get(language, ("AM", "PM"))
120
+ suffix = pm if is_pm else am
121
+
122
+ if language in ("zh", "ja"):
123
+ return f"{suffix} {time_str}"
124
+ if suffix:
125
+ return f"{time_str} {suffix}"
126
+ return time_str
127
+
128
+ def __eq__(self, other):
129
+ if isinstance(other, MPTime):
130
+ return self._time == other._time
131
+ return NotImplemented
132
+
133
+ def __lt__(self, other):
134
+ if isinstance(other, MPTime):
135
+ return self._time < other._time
136
+ return NotImplemented
137
+
138
+ def __hash__(self):
139
+ return hash(self._time)
140
+
141
+ def __str__(self):
142
+ return self.to_string("en")
143
+
144
+ def __repr__(self):
145
+ return (
146
+ f"MPTime({self._time.hour}, {self._time.minute}, {self._time.second})"
147
+ )
@@ -0,0 +1,18 @@
1
+ #
2
+ # SPDX-FileCopyrightText: 2024 John Samuel <johnsamuelwrites@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: GPL-3.0-or-later
5
+ #
6
+
7
+ """Shared helpers for datetime resource loading."""
8
+
9
+ import json
10
+ from pathlib import Path
11
+
12
+
13
+ def load_datetime_resource(filename):
14
+ """Load a JSON resource from multilingual datetime resources."""
15
+ resources_dir = Path(__file__).parent.parent / "resources" / "datetime"
16
+ resource_path = resources_dir / filename
17
+ with open(resource_path, "r", encoding="utf-8") as resource_file:
18
+ return json.load(resource_file)
@@ -0,0 +1,158 @@
1
+ #
2
+ # SPDX-FileCopyrightText: 2022 John Samuel <johnsamuelwrites@gmail.com>
3
+ #
4
+ # SPDX-License-Identifier: GPL-3.0-or-later
5
+ #
6
+
7
+ """Exceptions"""
8
+
9
+
10
+ class InvalidNumeralCharacterError(Exception):
11
+ """
12
+ Exception raised when a character is not a valid digit
13
+ """
14
+
15
+ def __init__(self, message):
16
+ message = "Invalid numeral: " + message
17
+ super().__init__(message)
18
+
19
+
20
+ class MultipleLanguageCharacterMixError(Exception):
21
+ """
22
+ Exception raised when a numeral string contains mix of characters
23
+ from different languages
24
+ """
25
+
26
+ def __init__(self, message):
27
+ message = "Mix of characters: " + message
28
+ super().__init__(message)
29
+
30
+
31
+ class DifferentNumeralTypeError(Exception):
32
+ """
33
+ Exception raised when an operation is performed on different
34
+ types of numeral type
35
+ """
36
+
37
+ def __init__(self, message):
38
+ message = "Invalid operation (different numeral type): " + message
39
+ super().__init__(message)
40
+
41
+
42
+ class UnknownKeywordError(Exception):
43
+ """
44
+ Exception raised when a keyword string is not found in any language
45
+ """
46
+
47
+ def __init__(self, message):
48
+ message = "Unknown keyword: " + message
49
+ super().__init__(message)
50
+
51
+
52
+ class AmbiguousKeywordError(Exception):
53
+ """
54
+ Exception raised when a keyword matches multiple concepts
55
+ """
56
+
57
+ def __init__(self, message):
58
+ message = "Ambiguous keyword: " + message
59
+ super().__init__(message)
60
+
61
+
62
+ class UnsupportedLanguageError(Exception):
63
+ """
64
+ Exception raised when a requested language is not in the registry
65
+ """
66
+
67
+ def __init__(self, message):
68
+ message = "Unsupported language: " + message
69
+ super().__init__(message)
70
+
71
+
72
+ class InvalidDateError(Exception):
73
+ """
74
+ Exception raised for malformed multilingual dates
75
+ """
76
+
77
+ def __init__(self, message):
78
+ message = "Invalid date: " + message
79
+ super().__init__(message)
80
+
81
+
82
+ class LexerError(Exception):
83
+ """
84
+ Base exception for lexer errors
85
+ """
86
+
87
+ def __init__(self, message, line=None, column=None):
88
+ location = ""
89
+ if line is not None:
90
+ location = f" at line {line}"
91
+ if column is not None:
92
+ location += f", column {column}"
93
+ message = "Lexer error" + location + ": " + message
94
+ self.line = line
95
+ self.column = column
96
+ super().__init__(message)
97
+
98
+
99
+ class UnexpectedTokenError(LexerError):
100
+ """
101
+ Exception raised for unexpected tokens during lexing
102
+ """
103
+
104
+ def __init__(self, message, line=None, column=None):
105
+ super().__init__("Unexpected token: " + message, line, column)
106
+
107
+
108
+ class ParseError(LexerError):
109
+ """
110
+ Base exception for parser errors
111
+ """
112
+
113
+ def __init__(self, message, line=None, column=None):
114
+ super().__init__("Parse error: " + message, line, column)
115
+
116
+
117
+ class SemanticError(Exception):
118
+ """
119
+ Exception for semantic analysis errors
120
+ """
121
+
122
+ def __init__(self, message, line=None, column=None):
123
+ location = ""
124
+ if line is not None:
125
+ location = f" at line {line}"
126
+ if column is not None:
127
+ location += f", column {column}"
128
+ self.line = line
129
+ self.column = column
130
+ message = "Semantic error" + location + ": " + message
131
+ super().__init__(message)
132
+
133
+
134
+ class CodeGenerationError(Exception):
135
+ """
136
+ Exception for code generation errors
137
+ """
138
+
139
+ def __init__(self, message, line=None, column=None):
140
+ location = ""
141
+ if line is not None:
142
+ location = f" at line {line}"
143
+ if column is not None:
144
+ location += f", column {column}"
145
+ self.line = line
146
+ self.column = column
147
+ message = "Code generation error" + location + ": " + message
148
+ super().__init__(message)
149
+
150
+
151
+ class RuntimeExecutionError(Exception):
152
+ """
153
+ Exception for runtime execution errors
154
+ """
155
+
156
+ def __init__(self, message):
157
+ message = "Runtime error: " + message
158
+ super().__init__(message)