python-plus-plus/ppp/ppp_interpreter.ppp
germax26 66cd54e532
Move .ppp files into ppp/ directory
I plan to make a few changes to my language, and I will then update
these files later. I am moving them because I don't want them
polluting the main directory. I probably should have done this to
begin with, but better late than never.
2024-08-11 11:35:49 +10:00

288 lines
11 KiB
Plaintext

import "ppp_tokens.ppp";
import "ppp_lexer.ppp";
import "ppp_ast.ppp";
import "ppp_parser.ppp";
import "ppp_types.ppp";
import "ppp_object.ppp";
import "ppp_stdlib.ppp";
enum VariableState {
Declared(Type, Object),
Undeclared(Type),
Constant(Type, Object)
}
func declared_from_obj(obj: Object) -> VariableState return VariableState.Declared(object_get_type(obj), obj);
type Module = dict[str, VariableState];
struct Program {
modules: dict[str, Module],
contexts: dict[str, VariableState][]
}
func program_exists(program: Program, name: str) -> bool {
for context in program.contexts do {
for name_ in context do {
if name_ == name return true;
}
}
return false;
}
func program_access_variable(program: Program, name: str) -> Object {
i: int = len(program.contexts) - 1;
while i >= 0 do {
context: dict[str, VariableState] = program.contexts[i];
for name_ in context do {
if name_ == name do {
value: VariableState = context[name];
match value in {
case Declared(_, value) return value;
case Const(_, value) return value;
case Undeclared(_) assert false, "'%s' is not defined!" % name;
case _ assert false, "Unimplemented program_access_variable %s" % value;
}
}
}
i = i - 1;
}
assert false, "'%s' is not defined!" % name;
}
func program_declare_variable(program: Program, name: str, type_: Type) {
for name_ in program.contexts[len(program.contexts)-1] assert name_ != name, "'%s' has already been declared!" % name;
program.contexts[len(program.contexts)-1][name] = VariableState.Undeclared(type_);
}
func program_assign_variable(program: Program, name: str, value: Object) {
i: int = len(program.contexts) - 1;
while i >= 0 do {
context: dict[str, VariableState] = program.contexts[i];
for name_ in context do {
if name_ == name do {
variable_type: Type;
match context[name] in {
case Undeclared(type_) variable_type = type_;
case _ assert false, "Unimplemented program_assign_variable %s" % context[name];
}
assert type_is_subtype_of(object_get_type(value), variable_type), "In the assignment of '%s', expected value of type '%s', but got a value of type '%s'!" % (name, type_represent(variable_type, type_represent(object_get_type(value))));
context[name] = VariableState.Declared(variable_type, value);
return none;
}
}
i = i - 1;
}
assert false, "'%s' doesn't exist!" % name;
}
func program_declare_and_assign_variable(program: Program, name: str, value: Object) {
program_declare_variable(program, name, object_get_type(value));
program_assign_variable(program, name, value);
}
func calculate_expression(expression: Expression, program: Program) -> Object {
match expression in {
case String(string) return Object.Str(string);
case Array(array_) do {
if len(array_) == 0 return Object.List(Type.List(Type.Variable("")), []);
elements_type: Type;
array_elements: Object[] = [];
for i in range(0, len(array_)) do {
element: Object = calculate_expression(array_[i], program);
if i == 0 elements_type = object_get_type(element);
else assert type_is_subtype_of(object_get_type(element), elements_type), "Array element invalid type";
array_elements = array_elements + [element];
}
assert false, "array %s" % array_elements;
}
case Dictionary(dict_) do {
if len(dict_) == 0 return Object.Dictionary(Type.Dictionary(Type.Variable(""), Type.Variable("")), {});
key_type: Type;
value_type: Type;
dict: dict[Object, Object] = {};
for i in range(0, len(dict_)) do {
key: Object = calculate_expression(dict_[i][0], program);
value: Object = calculate_expression(dict_[i][1], program);
if i == 0 do {
key_type = object_get_type(key);
value_type = object_get_type(value);
} else {
assert type_is_subtype_of(object_get_type(key), key_type), "Dict element invalid key type";
assert type_is_subtype_of(object_get_type(value), value_type), "Dict element invalid value type";
}
dict[key] = value;
}
assert false, "dict %s" % dict;
}
case FieldAccess(expression_, field) do {
value: Object = calculate_expression(expression_, program);
match value in {
case Type(type_) match type_ in {
case Enum(name, members, _) do {
for member in members do {
if member == field do {
if len(members[field]) == 0 return Object.EnumValue(type_, field, []);
func return_member(name: str, parameters: (str, Type)[], return_type: Type, statement: Statement, args: Object[]) -> Object {
match return_type in {
case Enum(_, _, _) do {}
case _ assert false, "Unreachable";
}
assert len(args) == len(parameters), "%s.%s expected %s arguments but got %s!" % (type_represent(type_), field, int_to_str(len(parameters)), int_to_str(len(args)));
for i in range(0, len(args)) assert type_is_subtype_of(object_get_type(args[i]), parameters[i][1]);
return Object.EnumValue(return_type, name, args);
}
return Object.Function(Type.Function(members[field], type_), (field, [("", member_type) for member_type in members[field]], type_, Statement.Statements([]), return_member));
}
}
assert false, "g";
}
case _ assert false, "Unimplemented calculate_expression field access type %s" % type_represent(type_);
}
case _ assert false, "Unimplemented calculate_expression field access %s" % value;
}
}
case Variable(name) return program_access_variable(program, name);
case _ assert false, "Unimplemented calculate_expression %s" % expression;
}
}
func calculate_type_expression(expression: TypeExpression, program: Program, must_resolve: bool) -> Type {
match expression in {
case Name(name) do {
if !program_exists(program, name) && !must_resolve return Type.Variable(name);
type_obj: Object = program_access_variable(program, name);
match type_obj in {
case Type(type_) return type_;
case _ assert false, "Unimplemented %s" % type_obj;
}
}
case List(type_) return Type.List(calculate_type_expression(type_, program, must_resolve));
case Tuple(types) return Type.Tuple([calculate_type_expression(type_, program, must_resolve) for type_ in types]);
case _ assert false, "Unimplemented calculate_type_expression %s" % expression;
}
}
func update_types(type_: Type, program: Program) {
type_name: str;
match type_ in {
case Enum(name, _, _) type_name = name;
case Struct(name, _, _) type_name = name;
case _ assert false, "Unimplemented update_types %s" % type_represent(type_);
}
for context in program.contexts do {
for variable_ in context do {
match context[variable_] in {
case Declared(variable_type, value) match value in {
case Type(variable_type_) do {
assert variable_type == Type.Type;
type_fill(variable_type_, {type_name: type_}, []);
}
}
case Undeclared(_) do {}
case _ assert false, "Unimplemented update_types %s" % context[variable_];
}
}
}
}
enum Result {
Return(Object),
Continue,
Break,
Nothing
}
func interpret_statements(statements: Statement[], program: Program) -> Result {
for statement in statements do {
match statement in {
case Import(file_) do {
file_path: Object = calculate_expression(file_, program);
match file_path in {
case Str(file_path_str) do {
found: bool = false;
module: Module;
for module_ in program.modules do {
if module_ == file_path_str do {
found = true;
module = program.modules[module_];
}
}
if !found do {
print("Importing %s\n" % file_path_str);
module = interpret_file(file_path_str, program.modules);
}
for variable in module do program.contexts[0][variable] = module[variable];
if !found program.modules[file_path_str] = module;
}
case _ assert false, "Unimplemented interpret_statement import %s" % file_path;
}
}
case EnumDefinition(name, entries) do {
enum_type: Type = Type.Enum(name, {entry.name: [calculate_type_expression(type_, program, false) for type_ in entry.types] for entry in entries}, []);
program_declare_and_assign_variable(program, name, Object.Type(enum_type));
update_types(enum_type, program);
}
case FunctionDefinition(name, arguments_, return_type_, body) do {
func run_function(name: str, arguments: (str, Type)[], return_type: Type, body: Statement, args: Object[]) -> Object {
assert false, "run_function";
}
arguments: (str, Type)[] = [(argument.name, calculate_type_expression(argument.type_, program, true)) for argument in arguments_];
return_type: Type;
match return_type_ in {
case Some(type_) return_type = calculate_type_expression(type_, program, true);
case None return_type = Type.Void;
}
function_type: Type = Type.Function([argument[1] for argument in arguments], return_type);
object: Object = Object.Function(function_type, (name, arguments, return_type, body, run_function));
program_declare_and_assign_variable(program, name, object);
}
case StructDefinition(name, entries) do {
struct_type: Type = Type.Struct(name, {entry.name: calculate_type_expression(entry.type_, program, false) for entry in entries}, []);
program_declare_and_assign_variable(program, name, Object.Type(struct_type));
update_types(struct_type, program);
}
case Assignment(lhs, rhs, type_) do {
assert is_valid_target(lhs);
match lhs in {
case Variable(name) do {
value: Object = calculate_expression(rhs, program);
match type_ in {
case _ assert false, "uuuu %s" % type_;
}
}
case _ assert false, "Unimplemented interpret_statement assignment %s" % lhs;
}
}
case _ assert false, "Unimplemented interpret_statement %s" % statement;
}
}
return Result.Nothing;
}
func interpret_file(file_path: str, modules: dict[str, Module]) -> Module {
print("Parsing %s...\n" % file_path);
lexer: Lexer = lexer_from_file(file_path);
statements: Statement[] = [];
while !is_some_token(lexer_take_token(lexer, TokenContents.Eof)) statements = statements + [parse_statement(lexer)];
new_variables: dict[str, VariableState] = {};
for variable in variables new_variables[variable] = declared_from_obj(variables[variable]);
program: Program = Program{modules=modules, contexts=[new_variables, {}]};
print("Interpreting %s...\n" % file_path);
return_value: Result = interpret_statements(statements, program);
assert len(program.contexts) == 2;
match return_value in {
case Nothing do {}
case Return(_) assert false, "Cannot return from outside a function!";
case Continue assert false, "Cannot continue from outside a loop!";
case Break assert false, "Cannot break from outside a loop!";
case _ assert false, "Unimplemented interpret_file return_value";
}
return program.contexts[1];
}