Skip to content
This repository was archived by the owner on Nov 23, 2022. It is now read-only.
Open

test #133

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
14 changes: 11 additions & 3 deletions compiler/language_models/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import attr
import immutabledict

from . import declarable, argument_list, namespace as namespace_module
from . import declarable, argument_list, namespace as namespace_module, string
from ..libs import parser as parser_module

# pylint: disable=fixme
Expand Down Expand Up @@ -130,7 +130,7 @@ def execute(self, namespace):
# TODO: local variable formatting
if self.is_binary:
return b''.join(bytes(value.value) for value in self.values)
return ''.join(str(value.value) for value in self.values)
return string.String(''.join(str(value.value) for value in self.values))

@classmethod
def from_string(cls, value):
Expand Down Expand Up @@ -287,6 +287,11 @@ def execute(self, namespace):
try:
return namespace.lookup(self.name)
except KeyError as exc:
if self.annotation is not None:
# If there's an annotation, this is a type declaration.
# Put None as the value as a placeholder for now.
self.assign(namespace, None)
return None
raise NameError(f'name {self.name!r} is not defined') from exc

@classmethod
Expand Down Expand Up @@ -677,7 +682,10 @@ def execute(self, namespace):
key: arg.execute(namespace)
for key, arg in self.keyword_arguments.items()
}
return callable_value(*positional_arguments, **keyword_arguments) # noqa
try:
return callable_value(*positional_arguments, **keyword_arguments) # noqa
except TypeError as exc:
raise TypeError(f'problem with {callable_value!r}(*{positional_arguments!r}, **{keyword_arguments!r})') from exc

@property
def expressions(self):
Expand Down
26 changes: 11 additions & 15 deletions compiler/language_models/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,11 @@

from . import (
statement as statement_module,
namespace as namespace_module,
namespace as namespace_module, test, string,
)
from ..libs import parser as parser_module


class _String(str):
is_alpha = str.isalpha
is_digit = str.isdigit
is_numeric = str.isnumeric
is_decimal = str.isdecimal
is_alphanumeric = str.isalnum
is_space = str.isspace


builtin_namespace = namespace_module.Namespace()
builtin_namespace.declare('True', True)
builtin_namespace.declare('False', False)
Expand All @@ -31,8 +22,8 @@ class _String(str):
builtin_namespace.declare('Exception', Exception)
builtin_namespace.declare('print', print)
builtin_namespace.declare('Boolean', bool)
builtin_namespace.declare('String', _String)
builtin_namespace.declare('Character', str)
builtin_namespace.declare('String', string.String)
builtin_namespace.declare('Character', string.String)
builtin_namespace.declare('Integer', int)
builtin_namespace.declare('List', list)
builtin_namespace.declare('FrozenList', tuple)
Expand All @@ -49,15 +40,16 @@ class _String(str):
builtin_namespace.declare('staticmethod', staticmethod)
builtin_namespace.declare('classmethod', classmethod)
builtin_namespace.declare('abstract', lambda x: x) # don't bother implementing yet
builtin_namespace.declare('test', test.test)


@attr.s(frozen=True, slots=True)
class Module(parser_module.Symbol):
statements: typing.Sequence[statement_module.Statement] = attr.ib(converter=tuple)

@classmethod
def from_string(cls, code: str) -> Module:
cursor = parser_module.Cursor(code.splitlines())
def from_string(cls, path: typing.Sequence[str], code: str) -> Module:
cursor = parser_module.Cursor(code.splitlines(), path=path)

return cls.parse(cursor).last_symbol # noqa

Expand Down Expand Up @@ -101,7 +93,11 @@ def execute(self,
for statement in self.statements:
statement.execute(global_namespace).get_value()

get_outcome().get_value() # reraise any exception, with module added to traceback
outcome = get_outcome()

if isinstance(outcome, statement_module.Raise.Outcome):
print(outcome)
outcome.get_value() # reraise any exception, with module added to traceback

return global_namespace.as_object()

Expand Down
13 changes: 11 additions & 2 deletions compiler/language_models/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def register_module(self, path: typing.Sequence[str], module: module_module.Modu
def register_module_from_string(self, path: typing.Sequence[str], module_string: str):
self.register_module(
path,
module_module.Module.from_string(module_string),
module_module.Module.from_string(path, module_string),
)

def register_package(self, path: typing.Sequence[str], root_path: str):
Expand All @@ -36,14 +36,23 @@ def register_package(self, path: typing.Sequence[str], root_path: str):
path + file_path.relative_to(root_path).parts
] = file_path.read_bytes()

# Initialize all the modules.
for module_path in self.modules:
self.get_module(module_path)

def get_module(self, path: typing.Sequence[str]):
path = tuple(path)

if path not in self.initialized_modules:
if path not in self.modules:
raise ImportError(f'no such module {path}')

self.initialized_modules[path] = self.modules[path].execute(self, path)
module = self.modules[path]

if isinstance(module, module_module.Module):
module = module.execute(self, path)

self.initialized_modules[path] = module

return self.initialized_modules[path]

Expand Down
11 changes: 11 additions & 0 deletions compiler/language_models/string.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
import regex


class String(str):
is_alpha = str.isalpha
is_digit = str.isdigit
is_numeric = str.isnumeric
is_decimal = str.isdecimal
is_alphanumeric = str.isalnum
is_space = str.isspace

length = str.__len__


def unescape_text(escaped: str) -> str:
return _ESCAPE_TEXT_REGEX.sub(
_unescape_text,
Expand Down
23 changes: 23 additions & 0 deletions compiler/language_models/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import traceback
import typing


def test(test_class):
print(f'testing {test_class}')
instance = test_class()

for name in dir(test_class):
method = getattr(instance, name)
if name.startswith('test_') and isinstance(method, typing.Callable):
print(f' {name}:')

try:
method()
except AssertionError:
print(' FAILURE')
traceback.print_exc()
except Exception: # noqa, pylint: disable=broad-except
print(' ERROR')
traceback.print_exc()
else:
print(' SUCCESS')
4 changes: 3 additions & 1 deletion compiler/libs/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Cursor:
column: int = attr.ib(default=0)
last_symbol: typing.Optional[Symbol] = attr.ib(default=None)
block_depth: int = attr.ib(default=0)
path: str = attr.ib(default=('?',))

def line_text(self, line=None):
line = self.line if line is None else line
Expand Down Expand Up @@ -62,6 +63,7 @@ def new_from_symbol(self, symbol: Symbol):
column=column,
last_symbol=symbol,
block_depth=block_depth,
path=self.path,
)

def parse_one_symbol(self, one_of: typing.Sequence[typing.Type[Symbol]], fail=False) -> Cursor:
Expand Down Expand Up @@ -99,7 +101,7 @@ def parse_one_symbol(self, one_of: typing.Sequence[typing.Type[Symbol]], fail=Fa
)

def __str__(self):
heading = f'{self.line + 1}, {self.column + 1}: '
heading = f'{".".join(self.path)}:{self.line + 1}:{self.column + 1}: '
line_text = self.line_text()
pointer_indent = ' ' * (len(heading) + self.column)
return f'{heading}{line_text}\n{pointer_indent}^'
Expand Down
16 changes: 11 additions & 5 deletions compiler/meta/generic.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import typing

import attr
import immutabledict


@attr.s(frozen=True, slots=True)
class Generic:
class_factory: typing.Callable = attr.ib()
_cache: typing.MutableMapping = attr.ib(factory=dict)

def __getitem__(self, item):
if not isinstance(item, tuple):
item = (item,)
def __getitem__(self, *args, **kwargs):
if len(args) == 1 and isinstance(args[0], tuple):
args = args[0]
kwargs = immutabledict.immutabledict(kwargs)
item = (args, kwargs)

if item not in self._cache:
self._cache[item] = self.class_factory(*item)
try:
if item not in self._cache:
self._cache[item] = self.class_factory(*args, **kwargs)
except TypeError as exc:
raise TypeError(f'problem with {item!r}') from exc

return self._cache[item]
18 changes: 9 additions & 9 deletions src/sibilance/compiler/parsing/ast.sib
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,6 @@ class Import(Node):
path: FrozenList[String]


class Statement(Or[
Import,
ClassDefinition,
FunctionDefinition,
Expression,
]):
pass


class ArgumentsDefinition(Node):
positional_arguments: FrozenList[VariableDefinition]
keyword_arguments: FrozenMap[String, VariableDefinition]
Expand All @@ -103,6 +94,15 @@ class ClassDefinition(Node):
statements: FrozenList[Statement]


class Statement(Or[
Import,
ClassDefinition,
FunctionDefinition,
Expression,
]):
pass


class Module(Node):
statements: FrozenList[Statement]

Expand Down
29 changes: 29 additions & 0 deletions src/sibilance/compiler/parsing/ast_test.sib
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import .ast


@test
class CursorTest:
def test_advance_line(self):
cursor = ast.Cursor()
assert cursor.line == 1

cursor = cursor.advance("hello\nworld")
assert cursor.line == 2
cursor = cursor.advance("\n\n")
assert cursor.line == 4

def test_advance_column(self):
cursor = ast.Cursor()
assert cursor.column == 1

cursor = cursor.advance("he")
assert cursor.column == 3

cursor = cursor.advance("llo")
assert cursor.column == 6

cursor = cursor.advance("\nwo")
assert cursor.column == 3

cursor = cursor.advance("rld\n!")
assert cursor.column == 2