Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
ContenT
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ximper Linux
ContenT
Commits
ec4a018f
Commit
ec4a018f
authored
Feb 16, 2026
by
Roman Alifanov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add compile-time method validation
parent
2aab914d
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
227 additions
and
82 deletions
+227
-82
LANGUAGE_SPEC.md
LANGUAGE_SPEC.md
+29
-1
README.md
README.md
+1
-0
README_ru.md
README_ru.md
+1
-0
dispatch_codegen.py
bootstrap/dispatch_codegen.py
+178
-73
errors.py
bootstrap/errors.py
+18
-8
No files found.
LANGUAGE_SPEC.md
View file @
ec4a018f
...
@@ -907,7 +907,35 @@ Error: Missing closing brace
...
@@ -907,7 +907,35 @@ Error: Missing closing brace
--> main.ct:15:1
--> main.ct:15:1
```
```
Показывает все ошибки сразу.
### Проверка методов
Компилятор проверяет существование методов при компиляции:
```
Error: Unknown method 'nonexistent' for type 'array'. Available: filter, get, join, len, map, pop, push, set, shift, slice
--> main.ct:5:1
Error: Unknown method 'badMethod' for type 'fs'. Available: append, exists, list, mkdir, open, read, remove, write
--> main.ct:10:8
```
Проверяются методы для:
-
**Массивов**
—
`filter`
,
`get`
,
`join`
,
`len`
,
`map`
,
`pop`
,
`push`
,
`set`
,
`shift`
,
`slice`
-
**Словарей**
—
`del`
,
`get`
,
`has`
,
`keys`
,
`set`
-
**Строк**
—
`charAt`
,
`contains`
,
`ends`
,
`index`
,
`len`
,
`lower`
,
`replace`
,
`split`
,
`starts`
,
`substr`
,
`trim`
,
`upper`
-
**Файловых дескрипторов**
—
`close`
,
`read`
,
`readline`
,
`write`
,
`writeln`
-
**Stdlib namespaces**
:
-
`fs`
—
`append`
,
`exists`
,
`list`
,
`mkdir`
,
`open`
,
`read`
,
`remove`
,
`write`
-
`http`
—
`delete`
,
`get`
,
`post`
,
`put`
-
`json`
—
`parse`
,
`stringify`
-
`logger`
—
`debug`
,
`error`
,
`info`
,
`warn`
-
`regex`
—
`extract`
,
`match`
-
`args`
—
`count`
,
`get`
-
`shell`
—
`capture`
,
`exec`
,
`source`
-
`time`
—
`ms`
,
`now`
-
`math`
—
`abs`
,
`add`
,
`div`
,
`max`
,
`min`
,
`mod`
,
`mul`
,
`sub`
Показывает все ошибки сразу (без дубликатов).
---
---
...
...
README.md
View file @
ec4a018f
...
@@ -17,6 +17,7 @@
...
@@ -17,6 +17,7 @@
-
**Error handling**
—
`try/except/finally/throw/defer`
-
**Error handling**
—
`try/except/finally/throw/defer`
-
**String interpolation**
—
`"Hello, {name}!"`
-
**String interpolation**
—
`"Hello, {name}!"`
-
**@awk functions**
— compile to AWK for ~300x speedup on string/numeric operations
-
**@awk functions**
— compile to AWK for ~300x speedup on string/numeric operations
-
**Compile-time method validation**
— catches unknown methods before runtime
-
**Optimized output**
- no unnecessary subshells, inlined methods
-
**Optimized output**
- no unnecessary subshells, inlined methods
## Installation
## Installation
...
...
README_ru.md
View file @
ec4a018f
...
@@ -17,6 +17,7 @@
...
@@ -17,6 +17,7 @@
-
**Обработка ошибок**
—
`try/except/finally/throw/defer`
-
**Обработка ошибок**
—
`try/except/finally/throw/defer`
-
**Строковая интерполяция**
—
`"Привет, {name}!"`
-
**Строковая интерполяция**
—
`"Привет, {name}!"`
-
**@awk функции**
— компиляция в AWK для ускорения ~300x на строковых/числовых операциях
-
**@awk функции**
— компиляция в AWK для ускорения ~300x на строковых/числовых операциях
-
**Проверка методов при компиляции**
— выявление несуществующих методов до запуска
-
**Оптимизированный вывод**
— без лишних subshell, инлайнинг методов
-
**Оптимизированный вывод**
— без лишних subshell, инлайнинг методов
## Установка
## Установка
...
...
bootstrap/dispatch_codegen.py
View file @
ec4a018f
...
@@ -30,10 +30,68 @@ FILE_HANDLE_METHODS = {
...
@@ -30,10 +30,68 @@ FILE_HANDLE_METHODS = {
BUILTIN_NAMESPACES
=
{
"fs"
,
"http"
,
"json"
,
"logger"
,
"regex"
,
"args"
,
"shell"
,
"time"
,
"math"
}
BUILTIN_NAMESPACES
=
{
"fs"
,
"http"
,
"json"
,
"logger"
,
"regex"
,
"args"
,
"shell"
,
"time"
,
"math"
}
BUILTIN_FUNCS
=
{
"print"
,
"exit"
,
"len"
,
"range"
,
"ngrep"
,
"is_number"
,
"is_empty"
,
"chr"
,
"ord"
,
"assert"
,
"assert_eq"
}
BUILTIN_FUNCS
=
{
"print"
,
"exit"
,
"len"
,
"range"
,
"ngrep"
,
"is_number"
,
"is_empty"
,
"chr"
,
"ord"
,
"assert"
,
"assert_eq"
}
FS_METHODS
=
{
"read"
,
"write"
,
"append"
,
"exists"
,
"remove"
,
"mkdir"
,
"list"
,
"open"
}
HTTP_METHODS
=
{
"get"
,
"post"
,
"put"
,
"delete"
}
JSON_METHODS
=
{
"parse"
,
"stringify"
}
LOGGER_METHODS
=
{
"info"
,
"warn"
,
"error"
,
"debug"
}
REGEX_METHODS
=
{
"match"
,
"extract"
}
ARGS_METHODS
=
{
"count"
,
"get"
}
SHELL_METHODS
=
{
"exec"
,
"capture"
,
"source"
}
TIME_METHODS
=
{
"now"
,
"ms"
}
MATH_METHODS
=
{
"add"
,
"sub"
,
"mul"
,
"div"
,
"mod"
,
"min"
,
"max"
,
"abs"
}
NAMESPACE_METHODS
=
{
"fs"
:
FS_METHODS
,
"http"
:
HTTP_METHODS
,
"json"
:
JSON_METHODS
,
"logger"
:
LOGGER_METHODS
,
"regex"
:
REGEX_METHODS
,
"args"
:
ARGS_METHODS
,
"shell"
:
SHELL_METHODS
,
"time"
:
TIME_METHODS
,
"math"
:
MATH_METHODS
,
}
class
DispatchMixin
:
class
DispatchMixin
:
"""Mixin for method dispatch and assignment."""
"""Mixin for method dispatch and assignment."""
def
_report_unknown_method
(
self
,
obj_type
:
str
,
method
:
str
,
available
:
set
,
location
=
None
):
"""Report unknown method error."""
from
.errors
import
CompileError
available_str
=
", "
.
join
(
sorted
(
available
))
msg
=
f
"Unknown method '{method}' for type '{obj_type}'. Available: {available_str}"
filename
=
location
.
filename
if
location
else
"<unknown>"
line
=
location
.
line
if
location
else
0
col
=
location
.
column
if
location
else
0
self
.
errors
.
add
(
CompileError
(
msg
,
filename
,
line
,
col
))
def
_validate_namespace_method
(
self
,
namespace
:
str
,
method
:
str
,
location
=
None
)
->
bool
:
"""Validate that method exists for namespace. Returns True if valid."""
if
namespace
not
in
NAMESPACE_METHODS
:
return
True
methods
=
NAMESPACE_METHODS
[
namespace
]
if
method
not
in
methods
:
self
.
_report_unknown_method
(
namespace
,
method
,
methods
,
location
)
return
False
return
True
def
_validate_type_method
(
self
,
var_type
:
str
,
method
:
str
,
location
=
None
)
->
bool
:
"""Validate that method exists for variable type. Returns True if valid."""
type_methods
=
{
"array"
:
set
(
ARR_METHODS
.
keys
()),
"dict"
:
set
(
DICT_METHODS
.
keys
()),
"string"
:
set
(
STR_METHODS
.
keys
()),
"file_handle"
:
set
(
FILE_HANDLE_METHODS
.
keys
()),
}
if
var_type
not
in
type_methods
:
return
True
available
=
type_methods
[
var_type
]
if
method
not
in
available
:
self
.
_report_unknown_method
(
var_type
,
method
,
available
,
location
)
return
False
return
True
def
_emit_array_assign
(
self
,
target
:
str
):
def
_emit_array_assign
(
self
,
target
:
str
):
"""Emit array assignment from __CT_RET."""
"""Emit array assignment from __CT_RET."""
if
self
.
in_function
and
target
not
in
self
.
local_vars
and
target
not
in
self
.
global_vars
:
if
self
.
in_function
and
target
not
in
self
.
local_vars
and
target
not
in
self
.
global_vars
:
...
@@ -176,6 +234,7 @@ class DispatchMixin:
...
@@ -176,6 +234,7 @@ class DispatchMixin:
"""Generate method call assignment. Returns True if handled."""
"""Generate method call assignment. Returns True if handled."""
callee
=
stmt
.
value
.
callee
callee
=
stmt
.
value
.
callee
method
=
callee
.
member
if
isinstance
(
callee
,
MemberAccess
)
else
None
method
=
callee
.
member
if
isinstance
(
callee
,
MemberAccess
)
else
None
location
=
getattr
(
stmt
.
value
,
'location'
,
None
)
or
getattr
(
callee
,
'location'
,
None
)
if
method
in
(
"map"
,
"filter"
):
if
method
in
(
"map"
,
"filter"
):
args
=
[
self
.
generate_expr
(
arg
)
for
arg
in
stmt
.
value
.
arguments
if
not
isinstance
(
arg
,
Lambda
)]
args
=
[
self
.
generate_expr
(
arg
)
for
arg
in
stmt
.
value
.
arguments
if
not
isinstance
(
arg
,
Lambda
)]
...
@@ -189,32 +248,34 @@ class DispatchMixin:
...
@@ -189,32 +248,34 @@ class DispatchMixin:
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
return
True
return
True
# Handle this.field.method() - field holds an object reference
if
isinstance
(
callee
.
object
,
MemberAccess
)
and
isinstance
(
callee
.
object
.
object
,
ThisExpr
):
if
isinstance
(
callee
.
object
,
MemberAccess
)
and
isinstance
(
callee
.
object
.
object
,
ThisExpr
):
field_name
=
callee
.
object
.
member
field_name
=
callee
.
object
.
member
method
=
callee
.
member
method
=
callee
.
member
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
# Check field type
field_type
=
self
.
class_field_types
.
get
((
self
.
current_class
,
field_name
))
if
self
.
current_class
else
None
field_type
=
self
.
class_field_types
.
get
((
self
.
current_class
,
field_name
))
if
self
.
current_class
else
None
if
field_type
==
"array"
and
method
in
ARR_METHODS
:
if
field_type
==
"array"
:
arr_name
=
f
'"${{this}}_{field_name}"'
if
method
in
ARR_METHODS
:
self
.
emit
(
f
'{ARR_METHODS[method]} {arr_name} {args_str} >/dev/null'
.
strip
())
arr_name
=
f
'"${{this}}_{field_name}"'
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
self
.
emit
(
f
'{ARR_METHODS[method]} {arr_name} {args_str} >/dev/null'
.
strip
())
return
True
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
if
field_type
==
"dict"
and
method
in
DICT_METHODS
:
return
True
dict_ref
=
f
'"${{__CT_OBJ[
\\
"$this.{field_name}
\\
"]}}"'
else
:
self
.
emit
(
f
'{DICT_METHODS[method]} {dict_ref} {args_str} >/dev/null'
.
strip
())
self
.
_validate_type_method
(
"array"
,
method
,
location
)
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
if
field_type
==
"dict"
:
return
True
if
method
in
DICT_METHODS
:
dict_ref
=
f
'"${{__CT_OBJ[
\\
"$this.{field_name}
\\
"]}}"'
self
.
emit
(
f
'{DICT_METHODS[method]} {dict_ref} {args_str} >/dev/null'
.
strip
())
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
return
True
else
:
self
.
_validate_type_method
(
"dict"
,
method
,
location
)
if
field_type
==
"object"
:
if
field_type
==
"object"
:
# Object field - use dynamic dispatch
obj_ref
=
f
'${{__CT_OBJ["$this.{field_name}"]}}'
obj_ref
=
f
'${{__CT_OBJ["$this.{field_name}"]}}'
self
.
emit
(
f
'__ct_call_method "{obj_ref}" "{method}" {args_str} >/dev/null'
)
self
.
emit
(
f
'__ct_call_method "{obj_ref}" "{method}" {args_str} >/dev/null'
)
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
return
True
return
True
# Scalar field - fall through to default handling
return
False
return
False
if
isinstance
(
callee
.
object
,
Identifier
):
if
isinstance
(
callee
.
object
,
Identifier
):
...
@@ -222,40 +283,50 @@ class DispatchMixin:
...
@@ -222,40 +283,50 @@ class DispatchMixin:
method
=
callee
.
member
method
=
callee
.
member
if
obj_name
in
BUILTIN_NAMESPACES
:
if
obj_name
in
BUILTIN_NAMESPACES
:
self
.
_validate_namespace_method
(
obj_name
,
method
,
location
)
return
False
return
False
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
if
obj_name
in
self
.
array_vars
and
method
in
ARR_METHODS
:
if
obj_name
in
self
.
array_vars
:
func_name
=
ARR_METHODS
[
method
]
if
method
in
ARR_METHODS
:
if
method
==
"push"
and
len
(
args
)
==
1
:
func_name
=
ARR_METHODS
[
method
]
self
.
emit
(
f
'{obj_name}+=("{args[0]}")'
)
if
method
==
"push"
and
len
(
args
)
==
1
:
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
self
.
emit
(
f
'{obj_name}+=("{args[0]}")'
)
elif
method
in
(
"map"
,
"filter"
)
and
len
(
stmt
.
value
.
arguments
)
>=
1
:
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
first_arg
=
stmt
.
value
.
arguments
[
0
]
elif
method
in
(
"map"
,
"filter"
)
and
len
(
stmt
.
value
.
arguments
)
>=
1
:
if
isinstance
(
first_arg
,
Lambda
):
first_arg
=
stmt
.
value
.
arguments
[
0
]
lambda_name
=
self
.
generate_lambda
(
first_arg
)
if
isinstance
(
first_arg
,
Lambda
):
self
.
emit
(
f
'{func_name} "{obj_name}" "{lambda_name}"'
)
lambda_name
=
self
.
generate_lambda
(
first_arg
)
self
.
emit
(
f
'{func_name} "{obj_name}" "{lambda_name}"'
)
else
:
self
.
emit
(
f
'{func_name} "{obj_name}" {args_str}'
.
replace
(
' '
,
' '
))
self
.
array_vars
.
add
(
target
)
self
.
_emit_array_assign
(
target
)
else
:
else
:
self
.
emit
(
f
'{func_name} "{obj_name}" {args_str}'
.
replace
(
' '
,
' '
))
self
.
emit
(
f
'{func_name} "{obj_name}" {args_str}
>/dev/null
'
.
replace
(
' '
,
' '
))
self
.
array_vars
.
add
(
target
)
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
self
.
_emit_array_assign
(
target
)
return
True
else
:
else
:
self
.
_validate_type_method
(
"array"
,
method
,
location
)
if
obj_name
in
self
.
dict_vars
:
if
method
in
DICT_METHODS
:
func_name
=
DICT_METHODS
[
method
]
self
.
emit
(
f
'{func_name} "{obj_name}" {args_str} >/dev/null'
.
replace
(
' '
,
' '
))
self
.
emit
(
f
'{func_name} "{obj_name}" {args_str} >/dev/null'
.
replace
(
' '
,
' '
))
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
return
True
return
True
else
:
if
obj_name
in
self
.
dict_vars
and
method
in
DICT_METHODS
:
self
.
_validate_type_method
(
"dict"
,
method
,
location
)
func_name
=
DICT_METHODS
[
method
]
self
.
emit
(
f
'{func_name} "{obj_name}" {args_str} >/dev/null'
.
replace
(
' '
,
' '
))
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
return
True
if
obj_name
in
self
.
file_handle_vars
and
method
in
FILE_HANDLE_METHODS
:
if
obj_name
in
self
.
file_handle_vars
:
func_name
=
FILE_HANDLE_METHODS
[
method
]
if
method
in
FILE_HANDLE_METHODS
:
self
.
emit
(
f
'{func_name} "${obj_name}" {args_str} >/dev/null'
.
replace
(
' '
,
' '
))
func_name
=
FILE_HANDLE_METHODS
[
method
]
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
self
.
emit
(
f
'{func_name} "${obj_name}" {args_str} >/dev/null'
.
replace
(
' '
,
' '
))
return
True
self
.
emit_var_assign
(
target
,
'$__CT_RET'
)
return
True
else
:
self
.
_validate_type_method
(
"file_handle"
,
method
,
location
)
if
method
in
STR_METHODS
:
if
method
in
STR_METHODS
:
obj
=
self
.
generate_expr
(
callee
.
object
)
obj
=
self
.
generate_expr
(
callee
.
object
)
...
@@ -507,6 +578,7 @@ class DispatchMixin:
...
@@ -507,6 +578,7 @@ class DispatchMixin:
var_name
=
expr
.
callee
.
object
.
name
var_name
=
expr
.
callee
.
object
.
name
method
=
expr
.
callee
.
member
method
=
expr
.
callee
.
member
args
=
[
self
.
generate_expr
(
arg
)
for
arg
in
expr
.
arguments
]
args
=
[
self
.
generate_expr
(
arg
)
for
arg
in
expr
.
arguments
]
location
=
getattr
(
expr
,
'location'
,
None
)
or
getattr
(
expr
.
callee
,
'location'
,
None
)
if
var_name
in
self
.
array_vars
:
if
var_name
in
self
.
array_vars
:
if
method
==
"push"
:
if
method
==
"push"
:
...
@@ -519,10 +591,13 @@ class DispatchMixin:
...
@@ -519,10 +591,13 @@ class DispatchMixin:
elif
method
==
"set"
and
len
(
args
)
>=
2
:
elif
method
==
"set"
and
len
(
args
)
>=
2
:
self
.
emit
(
f
'{var_name}[{args[0]}]="{args[1]}"'
)
self
.
emit
(
f
'{var_name}[{args[0]}]="{args[1]}"'
)
return
True
return
True
elif
method
in
(
"len"
,
"shift"
,
"join"
,
"get"
,
"slice"
):
elif
method
in
(
"len"
,
"shift"
,
"join"
,
"get"
,
"slice"
,
"map"
,
"filter"
):
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
self
.
emit
(
f
'__ct_arr_{method} "{var_name}" {args_str} >/dev/null'
.
strip
())
self
.
emit
(
f
'__ct_arr_{method} "{var_name}" {args_str} >/dev/null'
.
strip
())
return
True
return
True
else
:
self
.
_validate_type_method
(
"array"
,
method
,
location
)
return
False
if
var_name
in
self
.
dict_vars
:
if
var_name
in
self
.
dict_vars
:
if
method
==
"set"
and
len
(
args
)
>=
2
:
if
method
==
"set"
and
len
(
args
)
>=
2
:
...
@@ -535,11 +610,18 @@ class DispatchMixin:
...
@@ -535,11 +610,18 @@ class DispatchMixin:
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
self
.
emit
(
f
'__ct_dict_{method} "{var_name}" {args_str} >/dev/null'
.
strip
())
self
.
emit
(
f
'__ct_dict_{method} "{var_name}" {args_str} >/dev/null'
.
strip
())
return
True
return
True
else
:
self
.
_validate_type_method
(
"dict"
,
method
,
location
)
return
False
if
var_name
in
self
.
file_handle_vars
and
method
in
FILE_HANDLE_METHODS
:
if
var_name
in
self
.
file_handle_vars
:
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
if
method
in
FILE_HANDLE_METHODS
:
self
.
emit
(
f
'{FILE_HANDLE_METHODS[method]} "${var_name}" {args_str}'
.
strip
())
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
return
True
self
.
emit
(
f
'{FILE_HANDLE_METHODS[method]} "${var_name}" {args_str}'
.
strip
())
return
True
else
:
self
.
_validate_type_method
(
"file_handle"
,
method
,
location
)
return
False
return
False
return
False
...
@@ -547,12 +629,13 @@ class DispatchMixin:
...
@@ -547,12 +629,13 @@ class DispatchMixin:
callee
=
expr
.
callee
callee
=
expr
.
callee
args
=
[
self
.
generate_expr
(
arg
)
for
arg
in
expr
.
arguments
]
args
=
[
self
.
generate_expr
(
arg
)
for
arg
in
expr
.
arguments
]
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
args_str
=
" "
.
join
([
f
'"{a}"'
for
a
in
args
])
location
=
getattr
(
expr
,
'location'
,
None
)
or
getattr
(
callee
,
'location'
,
None
)
if
isinstance
(
callee
,
Identifier
):
if
isinstance
(
callee
,
Identifier
):
return
self
.
_generate_builtin_call
(
callee
.
name
,
args
,
args_str
)
return
self
.
_generate_builtin_call
(
callee
.
name
,
args
,
args_str
)
if
isinstance
(
callee
,
MemberAccess
):
if
isinstance
(
callee
,
MemberAccess
):
return
self
.
_generate_member_call
(
callee
,
args
,
args_str
)
return
self
.
_generate_member_call
(
callee
,
args
,
args_str
,
location
)
return
""
return
""
...
@@ -585,7 +668,7 @@ class DispatchMixin:
...
@@ -585,7 +668,7 @@ class DispatchMixin:
else
:
else
:
return
f
'{name} {args_str}'
return
f
'{name} {args_str}'
def
_generate_member_call
(
self
,
callee
:
MemberAccess
,
args
:
list
,
args_str
:
str
)
->
str
:
def
_generate_member_call
(
self
,
callee
:
MemberAccess
,
args
:
list
,
args_str
:
str
,
location
=
None
)
->
str
:
"""Generate member access call."""
"""Generate member access call."""
obj
=
self
.
generate_expr
(
callee
.
object
)
obj
=
self
.
generate_expr
(
callee
.
object
)
method
=
callee
.
member
method
=
callee
.
member
...
@@ -597,37 +680,52 @@ class DispatchMixin:
...
@@ -597,37 +680,52 @@ class DispatchMixin:
return
f
'__ct_class_{self.current_class}_{method} "$this" {args_str}'
return
f
'__ct_class_{self.current_class}_{method} "$this" {args_str}'
if
isinstance
(
callee
.
object
,
Identifier
):
if
isinstance
(
callee
.
object
,
Identifier
):
result
=
self
.
_generate_stdlib_call
(
callee
.
object
.
name
,
method
,
args
,
args_str
)
result
=
self
.
_generate_stdlib_call
(
callee
.
object
.
name
,
method
,
args
,
args_str
,
location
)
if
result
:
if
result
:
return
result
return
result
if
isinstance
(
callee
.
object
,
MemberAccess
)
and
isinstance
(
callee
.
object
.
object
,
ThisExpr
):
if
isinstance
(
callee
.
object
,
MemberAccess
)
and
isinstance
(
callee
.
object
.
object
,
ThisExpr
):
result
=
self
.
_generate_this_field_call
(
callee
,
args_str
)
result
=
self
.
_generate_this_field_call
(
callee
,
args_str
,
location
)
if
result
:
if
result
:
return
result
return
result
if
isinstance
(
callee
.
object
,
MemberAccess
)
and
isinstance
(
callee
.
object
.
object
,
Identifier
):
if
isinstance
(
callee
.
object
,
MemberAccess
)
and
isinstance
(
callee
.
object
.
object
,
Identifier
):
result
=
self
.
_generate_var_field_call
(
callee
,
args_str
)
result
=
self
.
_generate_var_field_call
(
callee
,
args_str
,
location
)
if
result
:
if
result
:
return
result
return
result
var_name
=
callee
.
object
.
name
if
isinstance
(
callee
.
object
,
Identifier
)
else
None
var_name
=
callee
.
object
.
name
if
isinstance
(
callee
.
object
,
Identifier
)
else
None
if
var_name
and
var_name
in
self
.
array_vars
and
method
in
ARR_METHODS
:
if
var_name
and
var_name
in
self
.
array_vars
:
if
method
==
"push"
and
len
(
args
)
==
1
:
if
method
in
ARR_METHODS
:
return
f
'{var_name}+=("{args[0]}")'
if
method
==
"push"
and
len
(
args
)
==
1
:
return
f
'{ARR_METHODS[method]} "{var_name}" {args_str}'
.
strip
()
return
f
'{var_name}+=("{args[0]}")'
return
f
'{ARR_METHODS[method]} "{var_name}" {args_str}'
.
strip
()
else
:
self
.
_validate_type_method
(
"array"
,
method
,
location
)
if
var_name
and
var_name
in
self
.
dict_vars
:
if
method
in
DICT_METHODS
:
return
f
'{DICT_METHODS[method]} "{var_name}" {args_str}'
.
strip
()
else
:
self
.
_validate_type_method
(
"dict"
,
method
,
location
)
if
var_name
and
var_name
in
self
.
dict_vars
and
method
in
DICT_METHODS
:
if
var_name
and
var_name
in
self
.
file_handle_vars
:
return
f
'{DICT_METHODS[method]} "{var_name}" {args_str}'
.
strip
()
if
method
in
FILE_HANDLE_METHODS
:
return
f
'{FILE_HANDLE_METHODS[method]} "${var_name}" {args_str}'
.
strip
()
else
:
self
.
_validate_type_method
(
"file_handle"
,
method
,
location
)
if
method
in
STR_METHODS
:
if
method
in
STR_METHODS
:
return
f
'{STR_METHODS[method]} "{obj}" {args_str}'
.
strip
()
return
f
'{STR_METHODS[method]} "{obj}" {args_str}'
.
strip
()
return
f
'__ct_call_method "{obj}" "{method}" {args_str}'
return
f
'__ct_call_method "{obj}" "{method}" {args_str}'
def
_generate_stdlib_call
(
self
,
obj_name
:
str
,
method
:
str
,
args
:
list
,
args_str
:
str
)
->
str
:
def
_generate_stdlib_call
(
self
,
obj_name
:
str
,
method
:
str
,
args
:
list
,
args_str
:
str
,
location
=
None
)
->
str
:
"""Generate stdlib call. Returns None if not stdlib."""
"""Generate stdlib call. Returns None if not stdlib."""
if
obj_name
in
BUILTIN_NAMESPACES
:
self
.
_validate_namespace_method
(
obj_name
,
method
,
location
)
if
obj_name
==
"http"
:
if
obj_name
==
"http"
:
return
f
'__ct_http_{method} {args_str}'
return
f
'__ct_http_{method} {args_str}'
elif
obj_name
==
"fs"
:
elif
obj_name
==
"fs"
:
...
@@ -659,31 +757,34 @@ class DispatchMixin:
...
@@ -659,31 +757,34 @@ class DispatchMixin:
return
f
'__ct_math_{method} {args_str}'
return
f
'__ct_math_{method} {args_str}'
return
None
return
None
def
_generate_this_field_call
(
self
,
callee
:
MemberAccess
,
args_str
:
str
)
->
str
:
def
_generate_this_field_call
(
self
,
callee
:
MemberAccess
,
args_str
:
str
,
location
=
None
)
->
str
:
"""Generate this.field.method() call."""
"""Generate this.field.method() call."""
field_name
=
callee
.
object
.
member
field_name
=
callee
.
object
.
member
method
=
callee
.
member
method
=
callee
.
member
# Check field type if known
field_type
=
self
.
class_field_types
.
get
((
self
.
current_class
,
field_name
))
if
self
.
current_class
else
None
field_type
=
self
.
class_field_types
.
get
((
self
.
current_class
,
field_name
))
if
self
.
current_class
else
None
if
field_type
==
"array"
and
method
in
ARR_METHODS
:
if
field_type
==
"array"
:
arr_name
=
f
'"${{this}}_{field_name}"'
if
method
in
ARR_METHODS
:
return
f
'{ARR_METHODS[method]} {arr_name} {args_str}'
.
strip
()
arr_name
=
f
'"${{this}}_{field_name}"'
return
f
'{ARR_METHODS[method]} {arr_name} {args_str}'
.
strip
()
else
:
self
.
_validate_type_method
(
"array"
,
method
,
location
)
if
field_type
==
"dict"
and
method
in
DICT_METHODS
:
if
field_type
==
"dict"
:
dict_ref
=
f
'"${{__CT_OBJ[
\\
"$this.{field_name}
\\
"]}}"'
if
method
in
DICT_METHODS
:
return
f
'{DICT_METHODS[method]} {dict_ref} {args_str}'
.
strip
()
dict_ref
=
f
'"${{__CT_OBJ[
\\
"$this.{field_name}
\\
"]}}"'
return
f
'{DICT_METHODS[method]} {dict_ref} {args_str}'
.
strip
()
else
:
self
.
_validate_type_method
(
"dict"
,
method
,
location
)
if
field_type
==
"object"
:
if
field_type
==
"object"
:
# Object field - use dynamic dispatch
obj_ref
=
f
'${{__CT_OBJ["$this.{field_name}"]}}'
obj_ref
=
f
'${{__CT_OBJ["$this.{field_name}"]}}'
return
f
'__ct_call_method "{obj_ref}" "{method}" {args_str}'
return
f
'__ct_call_method "{obj_ref}" "{method}" {args_str}'
# Scalar/unknown field - return None to fall through to default handling
return
None
return
None
def
_generate_var_field_call
(
self
,
callee
:
MemberAccess
,
args_str
:
str
)
->
str
:
def
_generate_var_field_call
(
self
,
callee
:
MemberAccess
,
args_str
:
str
,
location
=
None
)
->
str
:
"""Generate var.field.method() call."""
"""Generate var.field.method() call."""
var_name
=
callee
.
object
.
object
.
name
var_name
=
callee
.
object
.
object
.
name
field_name
=
callee
.
object
.
member
field_name
=
callee
.
object
.
member
...
@@ -702,16 +803,20 @@ class DispatchMixin:
...
@@ -702,16 +803,20 @@ class DispatchMixin:
for
cls
in
self
.
classes
for
cls
in
self
.
classes
)
)
if
is_array_field
and
method
in
ARR_METHODS
:
if
is_array_field
:
return
f
'{ARR_METHODS[method]} "${{{var_name}}}_{field_name}" {args_str}'
.
strip
()
if
method
in
ARR_METHODS
:
return
f
'{ARR_METHODS[method]} "${{{var_name}}}_{field_name}" {args_str}'
.
strip
()
else
:
self
.
_validate_type_method
(
"array"
,
method
,
location
)
if
is_dict_field
and
method
in
DICT_METHODS
:
if
is_dict_field
:
return
f
'{DICT_METHODS[method]} "${{{var_name}}}_{field_name}" {args_str}'
.
strip
()
if
method
in
DICT_METHODS
:
return
f
'{DICT_METHODS[method]} "${{{var_name}}}_{field_name}" {args_str}'
.
strip
()
else
:
self
.
_validate_type_method
(
"dict"
,
method
,
location
)
if
is_object_field
:
if
is_object_field
:
# Object field - use dynamic dispatch
obj_ref
=
f
'${{__CT_OBJ["${{{var_name}}}.{field_name}"]}}'
obj_ref
=
f
'${{__CT_OBJ["${{{var_name}}}.{field_name}"]}}'
return
f
'__ct_call_method "{obj_ref}" "{method}" {args_str}'
return
f
'__ct_call_method "{obj_ref}" "{method}" {args_str}'
# Scalar/unknown field - return None to fall through to default handling
return
None
return
None
bootstrap/errors.py
View file @
ec4a018f
...
@@ -20,18 +20,28 @@ class CompileError:
...
@@ -20,18 +20,28 @@ class CompileError:
class
ErrorCollector
:
class
ErrorCollector
:
def
__init__
(
self
):
def
__init__
(
self
):
self
.
errors
:
List
[
CompileError
]
=
[]
self
.
errors
:
List
[
CompileError
]
=
[]
self
.
_seen
:
set
=
set
()
def
_error_key
(
self
,
msg
:
str
,
filename
:
str
,
line
:
int
,
column
:
int
)
->
tuple
:
return
(
msg
,
filename
,
line
,
column
)
def
add
(
self
,
error
:
CompileError
):
def
add
(
self
,
error
:
CompileError
):
self
.
errors
.
append
(
error
)
key
=
self
.
_error_key
(
error
.
message
,
error
.
filename
,
error
.
line
,
error
.
column
)
if
key
not
in
self
.
_seen
:
self
.
_seen
.
add
(
key
)
self
.
errors
.
append
(
error
)
def
add_error
(
self
,
message
:
str
,
filename
:
str
,
line
:
int
,
column
:
int
,
hint
:
str
=
None
):
def
add_error
(
self
,
message
:
str
,
filename
:
str
,
line
:
int
,
column
:
int
,
hint
:
str
=
None
):
self
.
errors
.
append
(
CompileError
(
key
=
self
.
_error_key
(
message
,
filename
,
line
,
column
)
message
=
message
,
if
key
not
in
self
.
_seen
:
filename
=
filename
,
self
.
_seen
.
add
(
key
)
line
=
line
,
self
.
errors
.
append
(
CompileError
(
column
=
column
,
message
=
message
,
hint
=
hint
filename
=
filename
,
))
line
=
line
,
column
=
column
,
hint
=
hint
))
def
has_errors
(
self
)
->
bool
:
def
has_errors
(
self
)
->
bool
:
return
len
(
self
.
errors
)
>
0
return
len
(
self
.
errors
)
>
0
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment