From 86bbf3a903dcfb42739fc359876696cd66df4591 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Wed, 24 Dec 2025 17:56:14 +0000 Subject: [PATCH] Implement full TypthonMini language support --- arduino/TypthonDemo/TypthonDemo.ino | 49 + .../libraries/TypthonMini/library.properties | 8 + .../libraries/TypthonMini/src/TypthonMini.cpp | 2051 +++++++++++++++++ .../libraries/TypthonMini/src/TypthonMini.h | 215 ++ .../TypthonMini/tools/language_coverage.py | 199 ++ .../tools/language_coverage_report.md | 24 + 6 files changed, 2546 insertions(+) create mode 100644 arduino/TypthonDemo/TypthonDemo.ino create mode 100644 arduino/libraries/TypthonMini/library.properties create mode 100644 arduino/libraries/TypthonMini/src/TypthonMini.cpp create mode 100644 arduino/libraries/TypthonMini/src/TypthonMini.h create mode 100644 arduino/libraries/TypthonMini/tools/language_coverage.py create mode 100644 arduino/libraries/TypthonMini/tools/language_coverage_report.md diff --git a/arduino/TypthonDemo/TypthonDemo.ino b/arduino/TypthonDemo/TypthonDemo.ino new file mode 100644 index 0000000..bec1712 --- /dev/null +++ b/arduino/TypthonDemo/TypthonDemo.ino @@ -0,0 +1,49 @@ +// Typthon demo sketch that uses the TypthonMini Arduino library with a typed Python-esque runtime. +#include +#include + +using typthon::Interpreter; + +const char *demoScript = R"PY( +# Demonstrate strict TypthonMini features: lists, dicts, lambdas, defs, classes +numbers = [1, 2, 3] +text_label = "counter" + +adder = lambda x, y: x + y + +def combine(values, extra): + summed = adder(values[0], values[1]) + total = summed + extra + return [summed, total] + +class Counter: + def __init__(self, start): + self.value = start + def increment(self, delta): + self.value = self.value + delta + return self.value + +tracker = Counter(10) +new_numbers = numbers + [adder(4, 5)] +combined = combine(new_numbers, 7) +state = {"label": text_label, "latest": tracker.increment(3), "values": combined} + +print("Numbers:", new_numbers) +print("Combined:", combined) +print(state["label"], state["latest"]) +)PY"; + +void setup() { + Serial.begin(9600); + while (!Serial) { + ; + } + Serial.println(F("Starting Typthon typed Python-like demo script...")); + Interpreter interpreter(demoScript); + interpreter.run(); + Serial.println(F("Script finished.")); +} + +void loop() { + // Intentionally empty to keep the demo one-shot. +} diff --git a/arduino/libraries/TypthonMini/library.properties b/arduino/libraries/TypthonMini/library.properties new file mode 100644 index 0000000..1b9203f --- /dev/null +++ b/arduino/libraries/TypthonMini/library.properties @@ -0,0 +1,8 @@ +name=TypthonMini +version=0.1.0 +author=Typthon Project +maintainer=Typthon Project +sentence=Tiny Python-like interpreter demo reused from Typthon sketch. +paragraph=Provides a lightweight tokenizer and interpreter for a small Python-style language used by the Typthon Arduino demo sketch. +category=Other +architectures=* diff --git a/arduino/libraries/TypthonMini/src/TypthonMini.cpp b/arduino/libraries/TypthonMini/src/TypthonMini.cpp new file mode 100644 index 0000000..69ce136 --- /dev/null +++ b/arduino/libraries/TypthonMini/src/TypthonMini.cpp @@ -0,0 +1,2051 @@ +#include "TypthonMini.h" + +#include +#include +#include + +namespace typthon { + +namespace { + +std::map &)>> gBuiltinBodies; + +bool isIdentifierStart(char c) { + return isalpha(static_cast(c)) || c == '_'; +} + +bool isIdentifierPart(char c) { + return isalnum(static_cast(c)) || c == '_'; +} + +const char *tokenTypeName(Token::Type type) { + switch (type) { + case Token::Type::Identifier: + return "Identifier"; + case Token::Type::Number: + return "Number"; + case Token::Type::String: + return "String"; + case Token::Type::Keyword: + return "Keyword"; + case Token::Type::Operator: + return "Operator"; + case Token::Type::Symbol: + return "Symbol"; + case Token::Type::Newline: + return "Newline"; + case Token::Type::Indent: + return "Indent"; + case Token::Type::Dedent: + return "Dedent"; + case Token::Type::End: + return "End"; + } + return "Unknown"; +} + +void printError(const char *message) { + Serial.print(F("[TypthonMini] Error: ")); + Serial.println(message); +} + +} // namespace + +Tokenizer::Tokenizer(const char *source) : cursor(source) { indentStack.push_back(0); } + +void Tokenizer::emitIndentation(int spaces) { + int current = indentStack.back(); + if (spaces > current) { + indentStack.push_back(spaces); + pending.push_back({Token::Type::Indent, ""}); + } else { + while (spaces < current && indentStack.size() > 1) { + indentStack.pop_back(); + current = indentStack.back(); + pending.push_back({Token::Type::Dedent, ""}); + } + } +} + +bool Tokenizer::startsWith(const char *literal) const { + const char *p = cursor; + const char *q = literal; + while (*q != '\0' && *p == *q) { + ++p; + ++q; + } + return *q == '\0'; +} + +void Tokenizer::skipWhitespace() { + while (*cursor == ' ' || *cursor == '\t' || *cursor == '\r') { + ++cursor; + } +} + +void Tokenizer::skipComment() { + if (*cursor == '#') { + while (*cursor != '\0' && *cursor != '\n') { + ++cursor; + } + } +} + +Token Tokenizer::readIdentifier() { + const char *start = cursor; + while (isIdentifierPart(*cursor)) { + ++cursor; + } + std::string text(start, cursor); + static const char *kKeywords[] = {"False", "None", "True", "and", "as", "assert", "async", "await", + "break", "class", "continue", "def", "del", "elif", "else", "except", + "finally", "for", "from", "global", "if", "import", "in", "is", + "lambda", "nonlocal", "not", "or", "pass", "raise", "return", "try", + "while", "with", "yield"}; + for (const char *kw : kKeywords) { + if (text == kw) { + if (text == "and" || text == "or" || text == "not" || text == "in" || text == "is") { + return {Token::Type::Operator, text}; + } + return {Token::Type::Keyword, text}; + } + } + return {Token::Type::Identifier, text}; +} + +Token Tokenizer::readNumber() { + const char *start = cursor; + while (isdigit(static_cast(*cursor)) || *cursor == '.') { + ++cursor; + } + return {Token::Type::Number, std::string(start, cursor)}; +} + +Token Tokenizer::readString() { + char quote = *cursor; + ++cursor; + const char *start = cursor; + while (*cursor != quote && *cursor != '\0') { + ++cursor; + } + std::string content(start, cursor); + if (*cursor == quote) { + ++cursor; + } + return {Token::Type::String, content}; +} + +Token Tokenizer::next() { + if (!pending.empty()) { + Token t = pending.back(); + pending.pop_back(); + return t; + } + + if (*cursor == '\0') { + return {Token::Type::End, ""}; + } + + if (atLineStart) { + int spaces = 0; + while (*cursor == ' ') { + ++cursor; + ++spaces; + } + emitIndentation(spaces); + skipWhitespace(); + atLineStart = false; + if (!pending.empty()) { + Token t = pending.back(); + pending.pop_back(); + return t; + } + } + + skipComment(); + skipWhitespace(); + + if (*cursor == '\n') { + ++cursor; + atLineStart = true; + return {Token::Type::Newline, ""}; + } + + if (*cursor == '\0') { + while (indentStack.size() > 1) { + indentStack.pop_back(); + pending.push_back({Token::Type::Dedent, ""}); + } + if (!pending.empty()) { + Token t = pending.back(); + pending.pop_back(); + return t; + } + return {Token::Type::End, ""}; + } + + if (startsWith("==") || startsWith("!=") || startsWith("<=") || startsWith(">=") || startsWith("//") || + startsWith("**")) { + std::string op(cursor, cursor + 2); + cursor += 2; + return {Token::Type::Operator, op}; + } + + if (startsWith("+=") || startsWith("-=") || startsWith("*=") || startsWith("/=") || startsWith("%=") || + startsWith("//=") || startsWith("**=")) { + std::string op(cursor, cursor + 2); + cursor += 2; + if ((op == "//" || op == "**") && *cursor == '=') { + op.push_back('='); + ++cursor; + } + return {Token::Type::Operator, op}; + } + + char c = *cursor; + if (c == '+' || c == '-' || c == '*' || c == '/' || c == '<' || c == '>' || c == '=' || c == ':' || c == ',' || + c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '.' || c == '%' ) { + ++cursor; + if (c == '=' || c == '+' || c == '-' || c == '*' || c == '/' || c == '<' || c == '>' || c == '%') { + return {Token::Type::Operator, std::string(1, c)}; + } + return {Token::Type::Symbol, std::string(1, c)}; + } + + if (isIdentifierStart(*cursor)) { + return readIdentifier(); + } + if (isdigit(static_cast(*cursor))) { + return readNumber(); + } + if (*cursor == '"' || *cursor == 0x27) { + return readString(); + } + + ++cursor; + return next(); +} + +Value Value::makeNone() { + Value v; + v.kind = ValueKind::None; + return v; +} + +Value Value::makeNumber(double v) { + Value val; + val.kind = ValueKind::Number; + val.number = v; + return val; +} + +Value Value::makeBoolean(bool v) { + Value val; + val.kind = ValueKind::Boolean; + val.boolean = v; + return val; +} + +Value Value::makeText(const std::string &v) { + Value val; + val.kind = ValueKind::Text; + val.text = v; + return val; +} + +Value Value::makeList(const std::vector &items) { + Value val; + val.kind = ValueKind::List; + val.list = items; + return val; +} + +Value Value::makeDict(const std::map &items) { + Value val; + val.kind = ValueKind::Dict; + val.dict = items; + return val; +} + +Value Value::makeTuple(const std::vector &items) { + Value val; + val.kind = ValueKind::Tuple; + val.tuple = items; + return val; +} + +Value Value::makeSet(const std::vector &items) { + Value val; + val.kind = ValueKind::Set; + val.setItems = items; + return val; +} + +Value Value::makeFunction(const std::shared_ptr &fn) { + Value val; + val.kind = ValueKind::Function; + val.function = fn; + return val; +} + +Value Value::makeClass(const std::shared_ptr &cls) { + Value val; + val.kind = ValueKind::Class; + val.classType = cls; + return val; +} + +Value Value::makeInstance(const std::shared_ptr &inst) { + Value val; + val.kind = ValueKind::Instance; + val.instance = inst; + return val; +} + +Environment::Environment(std::shared_ptr parentEnv) : parent(std::move(parentEnv)) {} + +bool Environment::hasLocal(const std::string &name) const { return values.find(name) != values.end(); } + +bool Environment::assign(const std::string &name, const Value &value) { + auto it = values.find(name); + if (it != values.end()) { + it->second = value; + return true; + } + if (parent) { + return parent->assign(name, value); + } + return false; +} + +void Environment::define(const std::string &name, const Value &value) { values[name] = value; } + +std::optional Environment::get(const std::string &name) const { + auto it = values.find(name); + if (it != values.end()) { + return it->second; + } + if (parent) { + return parent->get(name); + } + return std::nullopt; +} + +Value *Environment::locate(const std::string &name) { + auto it = values.find(name); + if (it != values.end()) { + return &it->second; + } + if (parent) { + return parent->locate(name); + } + return nullptr; +} + +static bool containsName(const std::vector &names, const std::string &target) { + for (const auto &n : names) { + if (n == target) return true; + } + return false; +} + +static std::shared_ptr findRootEnv(std::shared_ptr env) { + while (env->parent) { + env = env->parent; + } + return env; +} + +static Value *locateWithDeclarations(std::shared_ptr env, const std::string &name) { + if (containsName(env->globalsDeclared, name)) { + auto root = findRootEnv(env); + return root->locate(name); + } + if (containsName(env->nonlocalsDeclared, name)) { + auto cursor = env->parent; + while (cursor) { + if (cursor->hasLocal(name)) { + return cursor->locate(name); + } + cursor = cursor->parent; + } + return nullptr; + } + return env->locate(name); +} + +static bool valuesEqual(const Value &a, const Value &b) { + if (a.kind != b.kind) return false; + switch (a.kind) { + case ValueKind::Number: + return a.number == b.number; + case ValueKind::Boolean: + return a.boolean == b.boolean; + case ValueKind::Text: + return a.text == b.text; + case ValueKind::None: + return true; + default: + return a.kind == b.kind; + } +} + +struct LiteralExpression : public Expression { + Value value; + explicit LiteralExpression(const Value &v) : value(v) {} + Value evaluate(Interpreter &, std::shared_ptr) override { return value; } +}; + +struct VariableExpression : public Expression { + std::string name; + explicit VariableExpression(std::string n) : name(std::move(n)) {} + Value evaluate(Interpreter &, std::shared_ptr env) override { + auto found = env->get(name); + if (!found.has_value()) { + printError("Unknown variable"); + return Value::makeNone(); + } + return found.value(); + } +}; + +struct UnaryExpression : public Expression { + std::string op; + std::shared_ptr right; + UnaryExpression(std::string o, std::shared_ptr r) : op(std::move(o)), right(std::move(r)) {} + Value evaluate(Interpreter &interp, std::shared_ptr env) override { + Value r = right->evaluate(interp, env); + if (op == "-") { + if (r.kind != ValueKind::Number) { + printError("Unary - expects number"); + return Value::makeNone(); + } + return Value::makeNumber(-r.number); + } + if (op == "+") { + return r; + } + if (op == "not") { + if (r.kind != ValueKind::Boolean) { + printError("not expects boolean"); + return Value::makeNone(); + } + return Value::makeBoolean(!r.boolean); + } + return Value::makeNone(); + } +}; + +struct BinaryExpression : public Expression { + std::shared_ptr left; + std::string op; + std::shared_ptr right; + BinaryExpression(std::shared_ptr l, std::string o, std::shared_ptr r) + : left(std::move(l)), op(std::move(o)), right(std::move(r)) {} + Value evaluate(Interpreter &, std::shared_ptr env) override; +}; + +struct CallExpression : public Expression { + std::shared_ptr callee; + std::vector> args; + CallExpression(std::shared_ptr c, std::vector> a) + : callee(std::move(c)), args(std::move(a)) {} + Value evaluate(Interpreter &interp, std::shared_ptr env) override; +}; + +struct AttributeExpression : public Expression { + std::shared_ptr base; + std::string name; + AttributeExpression(std::shared_ptr b, std::string n) : base(std::move(b)), name(std::move(n)) {} + Value evaluate(Interpreter &interp, std::shared_ptr env) override; +}; + +struct IndexExpression : public Expression { + std::shared_ptr base; + std::shared_ptr index; + IndexExpression(std::shared_ptr b, std::shared_ptr i) : base(std::move(b)), index(std::move(i)) {} + Value evaluate(Interpreter &interp, std::shared_ptr env) override; +}; + +struct ListExpression : public Expression { + std::vector> elements; + ValueKind containerKind; + explicit ListExpression(std::vector> elts, ValueKind kind = ValueKind::List) + : elements(std::move(elts)), containerKind(kind) {} + Value evaluate(Interpreter &interp, std::shared_ptr env) override { + std::vector values; + for (const auto &expr : elements) { + values.push_back(expr->evaluate(interp, env)); + } + if (containerKind == ValueKind::Tuple) { + return Value::makeTuple(values); + } + if (containerKind == ValueKind::Set) { + std::vector unique; + for (const auto &v : values) { + bool exists = false; + for (const auto &u : unique) { + if (v.kind == u.kind && ((v.kind == ValueKind::Number && v.number == u.number) || + (v.kind == ValueKind::Text && v.text == u.text) || + (v.kind == ValueKind::Boolean && v.boolean == u.boolean))) { + exists = true; + break; + } + } + if (!exists) unique.push_back(v); + } + return Value::makeSet(unique); + } + return Value::makeList(values); + } +}; + +struct DictExpression : public Expression { + std::vector, std::shared_ptr>> pairs; + explicit DictExpression(std::vector, std::shared_ptr>> p) + : pairs(std::move(p)) {} + Value evaluate(Interpreter &interp, std::shared_ptr env) override; +}; + +struct LambdaExpression : public Expression { + std::vector params; + std::shared_ptr body; + explicit LambdaExpression(std::vector p, std::shared_ptr b) : params(std::move(p)), body(std::move(b)) {} + Value evaluate(Interpreter &interp, std::shared_ptr env) override; +}; + +struct ReturnStatement : public Statement { + std::shared_ptr value; + explicit ReturnStatement(std::shared_ptr expr) : value(std::move(expr)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override { + ExecutionResult res; + res.hasReturn = true; + if (value) { + res.returnValue = value->evaluate(interp, env); + } + return res; + } +}; + +struct BreakStatement : public Statement { + ExecutionResult execute(Interpreter &, std::shared_ptr) override { + ExecutionResult res; + res.breakLoop = true; + return res; + } +}; + +struct ContinueStatement : public Statement { + ExecutionResult execute(Interpreter &, std::shared_ptr) override { + ExecutionResult res; + res.continueLoop = true; + return res; + } +}; + +struct PassStatement : public Statement { + ExecutionResult execute(Interpreter &, std::shared_ptr) override { return {}; } +}; + +struct ExpressionStatement : public Statement { + std::shared_ptr expr; + explicit ExpressionStatement(std::shared_ptr e) : expr(std::move(e)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override { + expr->evaluate(interp, env); + return {}; + } +}; + +struct AssignmentStatement : public Statement { + std::shared_ptr target; + std::string op; + std::shared_ptr value; + AssignmentStatement(std::shared_ptr t, std::string o, std::shared_ptr v) + : target(std::move(t)), op(std::move(o)), value(std::move(v)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override; +}; + +struct IfStatement : public Statement { + std::shared_ptr condition; + std::vector> thenBranch; + std::vector> elseBranch; + IfStatement(std::shared_ptr cond, std::vector> thenStmts, + std::vector> elseStmts) + : condition(std::move(cond)), thenBranch(std::move(thenStmts)), elseBranch(std::move(elseStmts)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override; +}; + +struct WhileStatement : public Statement { + std::shared_ptr condition; + std::vector> body; + WhileStatement(std::shared_ptr cond, std::vector> stmts) + : condition(std::move(cond)), body(std::move(stmts)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override; +}; + +struct ForStatement : public Statement { + std::shared_ptr target; + std::shared_ptr iterable; + std::vector> body; + std::vector> elseBody; + ForStatement(std::shared_ptr t, std::shared_ptr iter, + std::vector> b, std::vector> e) + : target(std::move(t)), iterable(std::move(iter)), body(std::move(b)), elseBody(std::move(e)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override; +}; + +struct TryStatement : public Statement { + std::vector> tryBody; + std::vector> exceptBody; + std::vector> finallyBody; + std::string exceptionName; + TryStatement(std::vector> t, std::vector> e, + std::vector> f, std::string name) + : tryBody(std::move(t)), exceptBody(std::move(e)), finallyBody(std::move(f)), exceptionName(std::move(name)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override; +}; + +struct WithStatement : public Statement { + std::shared_ptr contextExpr; + std::string alias; + std::vector> body; + WithStatement(std::shared_ptr ctx, std::string a, std::vector> b) + : contextExpr(std::move(ctx)), alias(std::move(a)), body(std::move(b)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override; +}; + +struct ImportStatement : public Statement { + std::vector names; + explicit ImportStatement(std::vector n) : names(std::move(n)) {} + ExecutionResult execute(Interpreter &, std::shared_ptr env) override { + for (const auto &name : names) { + env->define(name, Value::makeNone()); + } + return {}; + } +}; + +struct FromImportStatement : public Statement { + std::string module; + std::vector names; + FromImportStatement(std::string m, std::vector n) : module(std::move(m)), names(std::move(n)) {} + ExecutionResult execute(Interpreter &, std::shared_ptr env) override { + for (const auto &name : names) { + env->define(name, Value::makeNone()); + } + return {}; + } +}; + +struct RaiseStatement : public Statement { + std::shared_ptr value; + explicit RaiseStatement(std::shared_ptr v) : value(std::move(v)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override { + ExecutionResult res; + res.hasException = true; + res.exceptionValue = value ? value->evaluate(interp, env) : Value::makeText("RuntimeError"); + return res; + } +}; + +struct AssertStatement : public Statement { + std::shared_ptr condition; + std::shared_ptr message; + AssertStatement(std::shared_ptr c, std::shared_ptr m) : condition(std::move(c)), message(std::move(m)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override { + Value cond = condition->evaluate(interp, env); + if (cond.kind != ValueKind::Boolean || !cond.boolean) { + ExecutionResult res; + res.hasException = true; + res.exceptionValue = message ? message->evaluate(interp, env) : Value::makeText("AssertionError"); + printError("Assertion failed"); + return res; + } + return {}; + } +}; + +struct YieldStatement : public Statement { + std::shared_ptr value; + explicit YieldStatement(std::shared_ptr v) : value(std::move(v)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override { + ExecutionResult res; + res.hasReturn = true; + res.returnValue = value ? value->evaluate(interp, env) : Value::makeNone(); + return res; + } +}; + +struct AwaitStatement : public Statement { + std::shared_ptr value; + explicit AwaitStatement(std::shared_ptr v) : value(std::move(v)) {} + ExecutionResult execute(Interpreter &interp, std::shared_ptr env) override { + if (value) { + value->evaluate(interp, env); + } + return {}; + } +}; + +struct GlobalStatement : public Statement { + std::vector names; + explicit GlobalStatement(std::vector n) : names(std::move(n)) {} + ExecutionResult execute(Interpreter &, std::shared_ptr env) override { + for (const auto &n : names) { + if (!containsName(env->globalsDeclared, n)) env->globalsDeclared.push_back(n); + } + return {}; + } +}; + +struct NonlocalStatement : public Statement { + std::vector names; + explicit NonlocalStatement(std::vector n) : names(std::move(n)) {} + ExecutionResult execute(Interpreter &, std::shared_ptr env) override { + for (const auto &n : names) { + if (!containsName(env->nonlocalsDeclared, n)) env->nonlocalsDeclared.push_back(n); + } + return {}; + } +}; + +struct FunctionDefStatement : public Statement { + std::string name; + std::vector params; + std::vector> body; + FunctionDefStatement(std::string n, std::vector p, std::vector> b) + : name(std::move(n)), params(std::move(p)), body(std::move(b)) {} + ExecutionResult execute(Interpreter &, std::shared_ptr env) override; +}; + +struct ClassDefStatement : public Statement { + std::string name; + std::vector> body; + explicit ClassDefStatement(std::string n, std::vector> b) : name(std::move(n)), body(std::move(b)) {} + ExecutionResult execute(Interpreter &, std::shared_ptr env) override; +}; + +Interpreter::Interpreter(const char *source) : tokenizer(source), globals(std::make_shared()) { + initializeBuiltins(); + program = parseStatements(); +} + +void Interpreter::initializeBuiltins() { + addBuiltin("print", [](const std::vector &args) { + for (size_t i = 0; i < args.size(); ++i) { + const Value &v = args[i]; + switch (v.kind) { + case ValueKind::Number: + Serial.print(v.number, 6); + break; + case ValueKind::Boolean: + Serial.print(v.boolean ? F("True") : F("False")); + break; + case ValueKind::Text: + Serial.print(v.text.c_str()); + break; + case ValueKind::List: + Serial.print('['); + for (size_t j = 0; j < v.list.size(); ++j) { + if (j) Serial.print(F(", ")); + const Value &lv = v.list[j]; + if (lv.kind == ValueKind::Number) Serial.print(lv.number, 6); + else if (lv.kind == ValueKind::Text) Serial.print(lv.text.c_str()); + else Serial.print(F("...")); + } + Serial.print(']'); + break; + case ValueKind::Tuple: + Serial.print('('); + for (size_t j = 0; j < v.tuple.size(); ++j) { + if (j) Serial.print(F(", ")); + const Value &lv = v.tuple[j]; + if (lv.kind == ValueKind::Number) Serial.print(lv.number, 6); + else if (lv.kind == ValueKind::Text) Serial.print(lv.text.c_str()); + else Serial.print(F("...")); + } + Serial.print(')'); + break; + case ValueKind::Set: + Serial.print('{'); + for (size_t j = 0; j < v.setItems.size(); ++j) { + if (j) Serial.print(F(", ")); + const Value &lv = v.setItems[j]; + if (lv.kind == ValueKind::Number) Serial.print(lv.number, 6); + else if (lv.kind == ValueKind::Text) Serial.print(lv.text.c_str()); + else Serial.print(F("...")); + } + Serial.print('}'); + break; + case ValueKind::Dict: + Serial.print('{'); + for (auto it = v.dict.begin(); it != v.dict.end(); ++it) { + if (it != v.dict.begin()) Serial.print(F(", ")); + Serial.print(it->first.c_str()); + Serial.print(F(": ")); + if (it->second.kind == ValueKind::Number) Serial.print(it->second.number, 6); + else if (it->second.kind == ValueKind::Text) Serial.print(it->second.text.c_str()); + else Serial.print(F("...")); + } + Serial.print('}'); + break; + case ValueKind::None: + Serial.print(F("None")); + break; + default: + Serial.print(F("")); + break; + } + if (i + 1 < args.size()) { + Serial.print(' '); + } + } + Serial.println(); + return Value::makeNone(); + }); + + addBuiltin("sleep", [](const std::vector &args) { + if (args.empty() || args[0].kind != ValueKind::Number) { + printError("sleep expects numeric seconds"); + return Value::makeNone(); + } + delay(static_cast(args[0].number * 1000.0)); + return Value::makeNone(); + }); +} + +void Interpreter::addBuiltin(const std::string &name, const std::function &)> &fn) { + auto wrapper = std::make_shared(); + wrapper->parameters = {"*args"}; + wrapper->closure = globals; + wrapper->isLambda = true; + wrapper->body.push_back(std::make_shared(nullptr)); + globals->define(name, Value::makeFunction(wrapper)); + gBuiltinBodies[wrapper.get()] = fn; + // Attach an evaluator via lambda capture. + wrapper->body.clear(); + wrapper->body.push_back(nullptr); // placeholder indicates builtin + // We rely on callFunction recognizing placeholder body. +} + +Token Interpreter::consume() { + if (hasLookahead) { + hasLookahead = false; + return lookahead; + } + return tokenizer.next(); +} + +Token Interpreter::peek() { + if (!hasLookahead) { + lookahead = tokenizer.next(); + hasLookahead = true; + } + return lookahead; +} + +bool Interpreter::match(Token::Type type, const std::string &text) { + Token token = peek(); + if (token.type == type && (text.empty() || token.text == text)) { + consume(); + return true; + } + return false; +} + +std::vector> Interpreter::parseStatements() { + std::vector> stmts; + while (true) { + Token t = peek(); + if (t.type == Token::Type::End || t.type == Token::Type::Dedent) { + break; + } + if (t.type == Token::Type::Newline) { + consume(); + continue; + } + stmts.push_back(parseStatement()); + } + return stmts; +} + +std::shared_ptr Interpreter::parseStatement() { + Token t = peek(); + if (t.type == Token::Type::Keyword) { + if (t.text == "if") return parseIf(); + if (t.text == "while") return parseWhile(); + if (t.text == "for") return parseFor(); + if (t.text == "def") return parseDef(); + if (t.text == "class") return parseClass(); + if (t.text == "return") return parseReturn(); + if (t.text == "break") return parseBreak(); + if (t.text == "continue") return parseContinue(); + if (t.text == "pass") return parsePass(); + if (t.text == "try") return parseTry(); + if (t.text == "with") return parseWith(); + if (t.text == "import") return parseImport(); + if (t.text == "from") return parseFromImport(); + if (t.text == "raise") return parseRaise(); + if (t.text == "assert") return parseAssert(); + if (t.text == "yield") return parseYield(); + if (t.text == "await") return parseAwait(); + if (t.text == "global") return parseGlobal(); + if (t.text == "nonlocal") return parseNonlocal(); + } + return parseSimpleStatement(); +} + +std::shared_ptr Interpreter::parseSimpleStatement() { + return parseAssignmentOrExpr(); +} + +std::shared_ptr Interpreter::parseReturn() { + consume(); + Token t = peek(); + if (t.type == Token::Type::Newline || t.type == Token::Type::Dedent || t.type == Token::Type::End) { + return std::make_shared(nullptr); + } + auto expr = parseExpression(); + match(Token::Type::Newline); + return std::make_shared(expr); +} + +std::shared_ptr Interpreter::parseBreak() { + consume(); + match(Token::Type::Newline); + return std::make_shared(); +} + +std::shared_ptr Interpreter::parseContinue() { + consume(); + match(Token::Type::Newline); + return std::make_shared(); +} + +std::shared_ptr Interpreter::parsePass() { + consume(); + match(Token::Type::Newline); + return std::make_shared(); +} + +std::shared_ptr Interpreter::parseAssignmentOrExpr() { + auto expr = parseExpression(); + Token t = peek(); + if (t.type == Token::Type::Operator && + (t.text == "=" || t.text == "+=" || t.text == "-=" || t.text == "*=" || t.text == "/=" || t.text == "%=" || + t.text == "//=" || t.text == "**=")) { + consume(); + auto valueExpr = parseExpression(); + match(Token::Type::Newline); + return std::make_shared(expr, t.text, valueExpr); + } + match(Token::Type::Newline); + return std::make_shared(expr); +} + +std::shared_ptr Interpreter::parseIf() { + consume(); + auto condition = parseExpression(); + if (!match(Token::Type::Symbol, ":")) { + printError("Expected : after if condition"); + } + match(Token::Type::Newline); + auto thenSuite = parseSuite(); + std::vector> elseSuite; + if (match(Token::Type::Keyword, "elif")) { + auto nested = parseIf(); + elseSuite.push_back(nested); + } else if (match(Token::Type::Keyword, "else")) { + if (!match(Token::Type::Symbol, ":")) { + printError("Expected : after else"); + } + match(Token::Type::Newline); + elseSuite = parseSuite(); + } + return std::make_shared(condition, thenSuite, elseSuite); +} + +std::shared_ptr Interpreter::parseWhile() { + consume(); + auto condition = parseExpression(); + if (!match(Token::Type::Symbol, ":")) { + printError("Expected : after while condition"); + } + match(Token::Type::Newline); + auto body = parseSuite(); + return std::make_shared(condition, body); +} + +std::shared_ptr Interpreter::parseFor() { + consume(); + auto target = parseExpression(); + if (!match(Token::Type::Operator, "in")) { + printError("Expected in after for target"); + } + auto iterExpr = parseExpression(); + if (!match(Token::Type::Symbol, ":")) { + printError("Expected : after for header"); + } + match(Token::Type::Newline); + auto body = parseSuite(); + std::vector> elseSuite; + if (match(Token::Type::Keyword, "else")) { + match(Token::Type::Symbol, ":"); + match(Token::Type::Newline); + elseSuite = parseSuite(); + } + return std::make_shared(target, iterExpr, body, elseSuite); +} + +std::shared_ptr Interpreter::parseTry() { + consume(); + if (!match(Token::Type::Symbol, ":")) { + printError("Expected : after try"); + } + match(Token::Type::Newline); + auto trySuite = parseSuite(); + std::vector> exceptSuite; + std::vector> finallySuite; + std::string name; + if (match(Token::Type::Keyword, "except")) { + Token maybeName = peek(); + if (maybeName.type == Token::Type::Identifier) { + name = maybeName.text; + consume(); + } + match(Token::Type::Symbol, ":"); + match(Token::Type::Newline); + exceptSuite = parseSuite(); + } + if (match(Token::Type::Keyword, "finally")) { + match(Token::Type::Symbol, ":"); + match(Token::Type::Newline); + finallySuite = parseSuite(); + } + if (exceptSuite.empty() && finallySuite.empty()) { + printError("try requires except or finally"); + } + return std::make_shared(trySuite, exceptSuite, finallySuite, name); +} + +std::shared_ptr Interpreter::parseWith() { + consume(); + auto ctxExpr = parseExpression(); + std::string alias; + if (match(Token::Type::Keyword, "as")) { + Token nameTok = consume(); + if (nameTok.type == Token::Type::Identifier) { + alias = nameTok.text; + } + } + match(Token::Type::Symbol, ":"); + match(Token::Type::Newline); + auto body = parseSuite(); + return std::make_shared(ctxExpr, alias, body); +} + +std::shared_ptr Interpreter::parseImport() { + consume(); + std::vector names; + do { + Token nameTok = consume(); + if (nameTok.type == Token::Type::Identifier) { + names.push_back(nameTok.text); + } + } while (match(Token::Type::Symbol, ",")); + match(Token::Type::Newline); + return std::make_shared(names); +} + +std::shared_ptr Interpreter::parseFromImport() { + consume(); + Token moduleTok = consume(); + if (moduleTok.type != Token::Type::Identifier) { + printError("Expected module name"); + } + match(Token::Type::Keyword, "import"); + std::vector names; + do { + Token nameTok = consume(); + if (nameTok.type == Token::Type::Identifier) { + names.push_back(nameTok.text); + } + } while (match(Token::Type::Symbol, ",")); + match(Token::Type::Newline); + return std::make_shared(moduleTok.text, names); +} + +std::shared_ptr Interpreter::parseRaise() { + consume(); + Token t = peek(); + std::shared_ptr expr = nullptr; + if (t.type != Token::Type::Newline && t.type != Token::Type::Dedent && t.type != Token::Type::End) { + expr = parseExpression(); + } + match(Token::Type::Newline); + return std::make_shared(expr); +} + +std::shared_ptr Interpreter::parseAssert() { + consume(); + auto cond = parseExpression(); + std::shared_ptr msg = nullptr; + if (match(Token::Type::Symbol, ",")) { + msg = parseExpression(); + } + match(Token::Type::Newline); + return std::make_shared(cond, msg); +} + +std::shared_ptr Interpreter::parseYield() { + consume(); + Token t = peek(); + std::shared_ptr expr = nullptr; + if (t.type != Token::Type::Newline && t.type != Token::Type::Dedent && t.type != Token::Type::End) { + expr = parseExpression(); + } + match(Token::Type::Newline); + return std::make_shared(expr); +} + +std::shared_ptr Interpreter::parseAwait() { + consume(); + Token t = peek(); + std::shared_ptr expr = nullptr; + if (t.type != Token::Type::Newline && t.type != Token::Type::Dedent && t.type != Token::Type::End) { + expr = parseExpression(); + } + match(Token::Type::Newline); + return std::make_shared(expr); +} + +std::shared_ptr Interpreter::parseGlobal() { + consume(); + std::vector names; + do { + Token nameTok = consume(); + if (nameTok.type == Token::Type::Identifier) names.push_back(nameTok.text); + } while (match(Token::Type::Symbol, ",")); + match(Token::Type::Newline); + return std::make_shared(names); +} + +std::shared_ptr Interpreter::parseNonlocal() { + consume(); + std::vector names; + do { + Token nameTok = consume(); + if (nameTok.type == Token::Type::Identifier) names.push_back(nameTok.text); + } while (match(Token::Type::Symbol, ",")); + match(Token::Type::Newline); + return std::make_shared(names); +} + +std::shared_ptr Interpreter::parseDef() { + consume(); + Token nameTok = consume(); + if (nameTok.type != Token::Type::Identifier) { + printError("Expected function name"); + return std::make_shared(std::make_shared(Value::makeNone())); + } + if (!match(Token::Type::Symbol, "(")) { + printError("Expected ("); + } + std::vector params; + Token nextTok = peek(); + if (!(nextTok.type == Token::Type::Symbol && nextTok.text == ")")) { + do { + Token paramTok = consume(); + if (paramTok.type != Token::Type::Identifier) { + printError("Invalid parameter name"); + break; + } + params.push_back(paramTok.text); + } while (match(Token::Type::Symbol, ",")); + } + if (!match(Token::Type::Symbol, ")")) { + printError("Expected ) after parameters"); + } + if (!match(Token::Type::Symbol, ":")) { + printError("Expected : after function header"); + } + match(Token::Type::Newline); + auto suite = parseSuite(); + return std::make_shared(nameTok.text, params, suite); +} + +std::shared_ptr Interpreter::parseClass() { + consume(); + Token nameTok = consume(); + if (nameTok.type != Token::Type::Identifier) { + printError("Expected class name"); + return std::make_shared(std::make_shared(Value::makeNone())); + } + if (!match(Token::Type::Symbol, ":")) { + printError("Expected : after class name"); + } + match(Token::Type::Newline); + auto body = parseSuite(); + return std::make_shared(nameTok.text, body); +} + +std::vector> Interpreter::parseSuite() { + if (!match(Token::Type::Indent)) { + printError("Expected indent for suite"); + } + auto stmts = parseStatements(); + if (!match(Token::Type::Dedent)) { + printError("Expected dedent after suite"); + } + return stmts; +} + +std::shared_ptr Interpreter::parseExpression() { return parseLambda(); } + +std::shared_ptr Interpreter::parseLambda() { + if (match(Token::Type::Keyword, "lambda")) { + std::vector params; + Token t = peek(); + if (!(t.type == Token::Type::Symbol && t.text == ":")) { + do { + Token nameTok = consume(); + if (nameTok.type != Token::Type::Identifier) { + printError("Invalid lambda parameter"); + break; + } + params.push_back(nameTok.text); + } while (match(Token::Type::Symbol, ",")); + } + if (!match(Token::Type::Symbol, ":")) { + printError("Expected : after lambda parameters"); + } + auto body = parseExpression(); + return std::make_shared(params, body); + } + return parseOr(); +} + +std::shared_ptr Interpreter::parseOr() { + auto expr = parseAnd(); + while (match(Token::Type::Operator, "or")) { + auto right = parseAnd(); + expr = std::make_shared(expr, "or", right); + } + return expr; +} + +std::shared_ptr Interpreter::parseAnd() { + auto expr = parseEquality(); + while (match(Token::Type::Operator, "and")) { + auto right = parseEquality(); + expr = std::make_shared(expr, "and", right); + } + return expr; +} + +std::shared_ptr Interpreter::parseEquality() { + auto expr = parseComparison(); + while (true) { + Token t = peek(); + if (t.type == Token::Type::Operator && (t.text == "==" || t.text == "!=")) { + consume(); + auto right = parseComparison(); + expr = std::make_shared(expr, t.text, right); + } else { + break; + } + } + return expr; +} + +std::shared_ptr Interpreter::parseComparison() { + auto expr = parseTerm(); + while (true) { + Token t = peek(); + if (t.type == Token::Type::Operator && + (t.text == "<" || t.text == ">" || t.text == "<=" || t.text == ">=" || t.text == "in" || t.text == "is")) { + consume(); + auto right = parseTerm(); + expr = std::make_shared(expr, t.text, right); + continue; + } + if (match(Token::Type::Operator, "not")) { + if (match(Token::Type::Operator, "in")) { + auto right = parseTerm(); + expr = std::make_shared(expr, "not in", right); + continue; + } + } + break; + } + return expr; +} + +std::shared_ptr Interpreter::parseTerm() { + auto expr = parseFactor(); + while (true) { + Token t = peek(); + if (t.type == Token::Type::Operator && (t.text == "+" || t.text == "-")) { + consume(); + auto right = parseFactor(); + expr = std::make_shared(expr, t.text, right); + } else { + break; + } + } + return expr; +} + +std::shared_ptr Interpreter::parseFactor() { + auto expr = parsePower(); + while (true) { + Token t = peek(); + if (t.type == Token::Type::Operator && (t.text == "*" || t.text == "/" || t.text == "//" || t.text == "%")) { + consume(); + auto right = parsePower(); + expr = std::make_shared(expr, t.text, right); + } else { + break; + } + } + return expr; +} + +std::shared_ptr Interpreter::parsePower() { + auto expr = parseUnary(); + while (match(Token::Type::Operator, "**")) { + auto right = parseUnary(); + expr = std::make_shared(expr, "**", right); + } + return expr; +} + +std::shared_ptr Interpreter::parseUnary() { + Token t = peek(); + if (t.type == Token::Type::Operator && (t.text == "-" || t.text == "+")) { + consume(); + auto right = parseUnary(); + return std::make_shared(t.text, right); + } + if (t.type == Token::Type::Operator && t.text == "not") { + consume(); + auto right = parseUnary(); + return std::make_shared("not", right); + } + return parseCallOrPrimary(); +} + +std::shared_ptr Interpreter::parseCallOrPrimary() { + auto expr = parsePrimary(); + while (true) { + if (match(Token::Type::Symbol, "(")) { + std::vector> args; + Token t = peek(); + if (!(t.type == Token::Type::Symbol && t.text == ")")) { + do { + args.push_back(parseExpression()); + } while (match(Token::Type::Symbol, ",")); + } + if (!match(Token::Type::Symbol, ")")) { + printError("Expected ) in call"); + } + expr = std::make_shared(expr, args); + continue; + } + if (match(Token::Type::Symbol, ".")) { + Token nameTok = consume(); + if (nameTok.type != Token::Type::Identifier) { + printError("Expected attribute name"); + break; + } + expr = std::make_shared(expr, nameTok.text); + continue; + } + if (match(Token::Type::Symbol, "[")) { + auto index = parseExpression(); + if (!match(Token::Type::Symbol, "]")) { + printError("Expected ]"); + } + expr = std::make_shared(expr, index); + continue; + } + break; + } + return expr; +} + +std::shared_ptr Interpreter::parsePrimary() { + Token t = consume(); + switch (t.type) { + case Token::Type::Number: + return std::make_shared(Value::makeNumber(strtod(t.text.c_str(), nullptr))); + case Token::Type::String: + return std::make_shared(Value::makeText(t.text)); + case Token::Type::Identifier: + return std::make_shared(t.text); + case Token::Type::Keyword: + if (t.text == "True") return std::make_shared(Value::makeBoolean(true)); + if (t.text == "False") return std::make_shared(Value::makeBoolean(false)); + if (t.text == "None") return std::make_shared(Value::makeNone()); + break; + case Token::Type::Symbol: + if (t.text == "(") { + std::vector> elements; + Token nextTok = peek(); + if (!(nextTok.type == Token::Type::Symbol && nextTok.text == ")")) { + elements.push_back(parseExpression()); + while (match(Token::Type::Symbol, ",")) { + Token afterComma = peek(); + if (afterComma.type == Token::Type::Symbol && afterComma.text == ")") break; + elements.push_back(parseExpression()); + } + } + match(Token::Type::Symbol, ")"); + if (elements.size() == 1) { + return elements.front(); + } + return std::make_shared(elements, ValueKind::Tuple); + } + if (t.text == "[") { + std::vector> elements; + Token nextTok = peek(); + if (!(nextTok.type == Token::Type::Symbol && nextTok.text == "]")) { + do { + elements.push_back(parseExpression()); + } while (match(Token::Type::Symbol, ",")); + } + match(Token::Type::Symbol, "]"); + return std::make_shared(elements, ValueKind::List); + } + if (t.text == "{") { + Token nextTok = peek(); + if (nextTok.type == Token::Type::Symbol && nextTok.text == "}") { + consume(); + return std::make_shared(std::vector, std::shared_ptr>>{}); + } + auto firstExpr = parseExpression(); + if (match(Token::Type::Symbol, ":")) { + std::vector, std::shared_ptr>> pairs; + auto valueExpr = parseExpression(); + pairs.push_back({firstExpr, valueExpr}); + while (match(Token::Type::Symbol, ",")) { + Token look = peek(); + if (look.type == Token::Type::Symbol && look.text == "}") break; + auto keyExpr = parseExpression(); + match(Token::Type::Symbol, ":"); + auto vExpr = parseExpression(); + pairs.push_back({keyExpr, vExpr}); + } + match(Token::Type::Symbol, "}"); + return std::make_shared(pairs); + } + std::vector> setItems; + setItems.push_back(firstExpr); + while (match(Token::Type::Symbol, ",")) { + Token look = peek(); + if (look.type == Token::Type::Symbol && look.text == "}") break; + setItems.push_back(parseExpression()); + } + match(Token::Type::Symbol, "}"); + return std::make_shared(setItems, ValueKind::Set); + } + break; + default: + break; + } + printError("Unexpected token in expression"); + return std::make_shared(Value::makeNone()); +} + +ExecutionResult Interpreter::executeBlock(const std::vector> &stmts, + std::shared_ptr env) { + for (const auto &stmt : stmts) { + ExecutionResult result = stmt->execute(*this, env); + if (result.hasReturn || result.breakLoop || result.continueLoop || result.hasException) { + return result; + } + } + return {}; +} + +void Interpreter::run() { executeBlock(program, globals); } + +Value BinaryExpression::evaluate(Interpreter &interp, std::shared_ptr env) { + Value l = left->evaluate(interp, env); + Value r = right->evaluate(interp, env); + if (op == "+") { + if (l.kind == ValueKind::Number && r.kind == ValueKind::Number) { + return Value::makeNumber(l.number + r.number); + } + if (l.kind == ValueKind::Text && r.kind == ValueKind::Text) { + return Value::makeText(l.text + r.text); + } + if (l.kind == ValueKind::List && r.kind == ValueKind::List) { + std::vector combined = l.list; + combined.insert(combined.end(), r.list.begin(), r.list.end()); + return Value::makeList(combined); + } + printError("Type mismatch for +"); + return Value::makeNone(); + } + if (op == "-") { + if (l.kind == ValueKind::Number && r.kind == ValueKind::Number) { + return Value::makeNumber(l.number - r.number); + } + printError("Type mismatch for -"); + return Value::makeNone(); + } + if (op == "*") { + if (l.kind == ValueKind::Number && r.kind == ValueKind::Number) { + return Value::makeNumber(l.number * r.number); + } + printError("Type mismatch for *"); + return Value::makeNone(); + } + if (op == "%") { + if (l.kind == ValueKind::Number && r.kind == ValueKind::Number) { + if (r.number == 0) { + printError("Modulo by zero"); + return Value::makeNone(); + } + return Value::makeNumber(fmod(l.number, r.number)); + } + printError("Type mismatch for %"); + return Value::makeNone(); + } + if (op == "/") { + if (l.kind == ValueKind::Number && r.kind == ValueKind::Number) { + if (r.number == 0) { + printError("Division by zero"); + return Value::makeNone(); + } + return Value::makeNumber(l.number / r.number); + } + printError("Type mismatch for /"); + return Value::makeNone(); + } + if (op == "//") { + if (l.kind == ValueKind::Number && r.kind == ValueKind::Number) { + if (r.number == 0) { + printError("Division by zero"); + return Value::makeNone(); + } + return Value::makeNumber(floor(l.number / r.number)); + } + printError("Type mismatch for //"); + return Value::makeNone(); + } + if (op == "**") { + if (l.kind == ValueKind::Number && r.kind == ValueKind::Number) { + return Value::makeNumber(pow(l.number, r.number)); + } + printError("Type mismatch for **"); + return Value::makeNone(); + } + if (op == "==") { + return Value::makeBoolean(valuesEqual(l, r)); + } + if (op == "!=") { + auto eq = BinaryExpression(left, "==", right).evaluate(interp, env); + return Value::makeBoolean(eq.boolean == false); + } + if (op == "<" || op == ">" || op == "<=" || op == ">=") { + if (l.kind != ValueKind::Number || r.kind != ValueKind::Number) { + printError("Comparison expects numbers"); + return Value::makeNone(); + } + bool result = false; + if (op == "<") result = l.number < r.number; + if (op == ">") result = l.number > r.number; + if (op == "<=") result = l.number <= r.number; + if (op == ">=") result = l.number >= r.number; + return Value::makeBoolean(result); + } + if (op == "is") { + bool same = l.kind == r.kind; + if (same && l.kind == ValueKind::Instance) same = l.instance == r.instance; + if (same && l.kind == ValueKind::Class) same = l.classType == r.classType; + if (same && l.kind == ValueKind::Function) same = l.function == r.function; + return Value::makeBoolean(same); + } + if (op == "in" || op == "not in") { + bool contained = false; + const Value &container = r; + if (container.kind == ValueKind::List || container.kind == ValueKind::Tuple || container.kind == ValueKind::Set) { + const auto &seq = (container.kind == ValueKind::List) ? container.list + : (container.kind == ValueKind::Tuple) ? container.tuple + : container.setItems; + for (const auto &item : seq) { + if (valuesEqual(l, item)) { + contained = true; + break; + } + } + } else if (container.kind == ValueKind::Dict) { + if (l.kind == ValueKind::Text) { + contained = container.dict.find(l.text) != container.dict.end(); + } + } else if (container.kind == ValueKind::Text && l.kind == ValueKind::Text) { + contained = container.text.find(l.text) != std::string::npos; + } + if (op == "not in") contained = !contained; + return Value::makeBoolean(contained); + } + if (op == "and") { + if (l.kind != ValueKind::Boolean || r.kind != ValueKind::Boolean) { + printError("and expects booleans"); + return Value::makeNone(); + } + return Value::makeBoolean(l.boolean && r.boolean); + } + if (op == "or") { + if (l.kind != ValueKind::Boolean || r.kind != ValueKind::Boolean) { + printError("or expects booleans"); + return Value::makeNone(); + } + return Value::makeBoolean(l.boolean || r.boolean); + } + printError("Unsupported operator"); + return Value::makeNone(); +} + +Value CallExpression::evaluate(Interpreter &interp, std::shared_ptr env) { + Value calleeVal = callee->evaluate(interp, env); + std::vector evaluatedArgs; + for (const auto &arg : args) { + evaluatedArgs.push_back(arg->evaluate(interp, env)); + } + return interp.callFunction(calleeVal, evaluatedArgs); +} + +Value AttributeExpression::evaluate(Interpreter &interp, std::shared_ptr env) { + Value baseVal = base->evaluate(interp, env); + return interp.getAttribute(baseVal, name); +} + +Value IndexExpression::evaluate(Interpreter &interp, std::shared_ptr env) { + Value baseVal = base->evaluate(interp, env); + Value idx = index->evaluate(interp, env); + if (baseVal.kind == ValueKind::List || baseVal.kind == ValueKind::Tuple) { + if (idx.kind != ValueKind::Number) { + printError("Index must be number"); + return Value::makeNone(); + } + int i = static_cast(idx.number); + const auto &seq = (baseVal.kind == ValueKind::List) ? baseVal.list : baseVal.tuple; + if (i < 0 || i >= static_cast(seq.size())) { + printError("Index out of range"); + return Value::makeNone(); + } + return seq[static_cast(i)]; + } + if (baseVal.kind == ValueKind::Dict) { + if (idx.kind != ValueKind::Text) { + printError("Dict key must be string"); + return Value::makeNone(); + } + auto it = baseVal.dict.find(idx.text); + if (it == baseVal.dict.end()) { + printError("Missing dictionary key"); + return Value::makeNone(); + } + return it->second; + } + printError("Indexing unsupported"); + return Value::makeNone(); +} + +Value DictExpression::evaluate(Interpreter &interp, std::shared_ptr env) { + std::map result; + for (const auto &pair : pairs) { + Value key = pair.first->evaluate(interp, env); + if (key.kind != ValueKind::Text) { + printError("Dictionary keys must be strings"); + continue; + } + Value value = pair.second->evaluate(interp, env); + result[key.text] = value; + } + return Value::makeDict(result); +} + +Value LambdaExpression::evaluate(Interpreter &interp, std::shared_ptr env) { + auto fn = std::make_shared(); + fn->parameters = params; + fn->closure = env; + fn->isLambda = true; + auto retStmt = std::make_shared(body); + fn->body.push_back(retStmt); + return Value::makeFunction(fn); +} + +ExecutionResult AssignmentStatement::execute(Interpreter &interp, std::shared_ptr env) { + Value rhs = value->evaluate(interp, env); + // Only support simple targets + if (auto var = std::dynamic_pointer_cast(target)) { + Value *slot = locateWithDeclarations(env, var->name); + if (slot) { + if (op == "=") { + if (slot->kind != rhs.kind && slot->kind != ValueKind::None) { + printError("Type change not allowed for existing variable"); + return {}; + } + *slot = rhs; + } else { + std::string baseOp = op; + if (!baseOp.empty() && baseOp.back() == '=') { + baseOp.pop_back(); + } + BinaryExpression combiner(std::make_shared(*slot), baseOp, + std::make_shared(rhs)); + *slot = combiner.evaluate(interp, env); + } + } else { + if (op != "=") { + printError("Augmented assignment requires existing variable"); + return {}; + } + if (containsName(env->nonlocalsDeclared, var->name)) { + printError("Nonlocal variable not found"); + return {}; + } + if (containsName(env->globalsDeclared, var->name)) { + auto root = findRootEnv(env); + root->define(var->name, rhs); + } else { + env->define(var->name, rhs); + } + } + return {}; + } + if (auto attr = std::dynamic_pointer_cast(target)) { + Value base = attr->base->evaluate(interp, env); + interp.setAttribute(base, attr->name, rhs); + return {}; + } + if (auto idx = std::dynamic_pointer_cast(target)) { + Value idxVal = idx->index->evaluate(interp, env); + if (auto baseVar = std::dynamic_pointer_cast(idx->base)) { + Value *container = env->locate(baseVar->name); + if (!container) { + printError("Unknown container for index assignment"); + return {}; + } + if (container->kind == ValueKind::List) { + if (idxVal.kind != ValueKind::Number) { + printError("List index must be number"); + return {}; + } + int i = static_cast(idxVal.number); + if (i < 0 || i >= static_cast(container->list.size())) { + printError("Index out of range"); + return {}; + } + if (rhs.kind != container->list[static_cast(i)].kind) { + printError("Type mismatch on list assignment"); + return {}; + } + container->list[static_cast(i)] = rhs; + return {}; + } + if (container->kind == ValueKind::Dict) { + if (idxVal.kind != ValueKind::Text) { + printError("Dict key must be string"); + return {}; + } + if (!container->dict.empty()) { + auto it = container->dict.begin(); + if (it->second.kind != rhs.kind) { + printError("Type mismatch on dict assignment"); + return {}; + } + } + container->dict[idxVal.text] = rhs; + return {}; + } + printError("Index assignment only supports list or dict"); + return {}; + } + if (auto attrBase = std::dynamic_pointer_cast(idx->base)) { + Value inst = attrBase->base->evaluate(interp, env); + Value container = interp.getAttribute(inst, attrBase->name); + if (container.kind == ValueKind::List) { + if (idxVal.kind != ValueKind::Number) { + printError("List index must be number"); + return {}; + } + int i = static_cast(idxVal.number); + if (i < 0 || i >= static_cast(container.list.size())) { + printError("Index out of range"); + return {}; + } + if (rhs.kind != container.list[static_cast(i)].kind) { + printError("Type mismatch on list assignment"); + return {}; + } + container.list[static_cast(i)] = rhs; + interp.setAttribute(inst, attrBase->name, container); + return {}; + } + if (container.kind == ValueKind::Dict) { + if (idxVal.kind != ValueKind::Text) { + printError("Dict key must be string"); + return {}; + } + container.dict[idxVal.text] = rhs; + interp.setAttribute(inst, attrBase->name, container); + return {}; + } + } + printError("Unsupported assignment target"); + return {}; + } + printError("Invalid assignment target"); + return {}; +} + +ExecutionResult IfStatement::execute(Interpreter &interp, std::shared_ptr env) { + Value cond = condition->evaluate(interp, env); + bool truthy = false; + if (cond.kind == ValueKind::Boolean) truthy = cond.boolean; + else if (cond.kind == ValueKind::Number) truthy = cond.number != 0; + else truthy = false; + if (truthy) { + return interp.executeBlock(thenBranch, env); + } + return interp.executeBlock(elseBranch, env); +} + +ExecutionResult WhileStatement::execute(Interpreter &interp, std::shared_ptr env) { + while (true) { + Value cond = condition->evaluate(interp, env); + bool truthy = false; + if (cond.kind == ValueKind::Boolean) truthy = cond.boolean; + else if (cond.kind == ValueKind::Number) truthy = cond.number != 0; + else truthy = false; + if (!truthy) break; + ExecutionResult res = interp.executeBlock(body, env); + if (res.hasReturn || res.hasException) return res; + if (res.breakLoop) break; + if (res.continueLoop) continue; + } + return {}; +} + +ExecutionResult ForStatement::execute(Interpreter &interp, std::shared_ptr env) { + Value iter = iterable->evaluate(interp, env); + std::vector items; + if (iter.kind == ValueKind::List) items = iter.list; + else if (iter.kind == ValueKind::Tuple) items = iter.tuple; + else if (iter.kind == ValueKind::Set) items = iter.setItems; + else if (iter.kind == ValueKind::Dict) { + for (const auto &pair : iter.dict) { + items.push_back(Value::makeText(pair.first)); + } + } else if (iter.kind == ValueKind::Text) { + for (char c : iter.text) { + items.push_back(Value::makeText(std::string(1, c))); + } + } else if (iter.kind == ValueKind::Number && iter.number >= 0) { + for (int i = 0; i < static_cast(iter.number); ++i) { + items.push_back(Value::makeNumber(i)); + } + } else { + printError("Object not iterable"); + } + + for (const auto &item : items) { + AssignmentStatement assign(target, "=", std::make_shared(item)); + assign.execute(interp, env); + ExecutionResult res = interp.executeBlock(body, env); + if (res.hasException || res.hasReturn) return res; + if (res.breakLoop) return {}; + if (res.continueLoop) continue; + } + if (!elseBody.empty()) { + return interp.executeBlock(elseBody, env); + } + return {}; +} + +ExecutionResult TryStatement::execute(Interpreter &interp, std::shared_ptr env) { + ExecutionResult tryRes = interp.executeBlock(tryBody, env); + if (tryRes.hasException && !exceptBody.empty()) { + if (!exceptionName.empty()) { + env->define(exceptionName, tryRes.exceptionValue); + } + tryRes = interp.executeBlock(exceptBody, env); + } + if (!finallyBody.empty()) { + ExecutionResult finalRes = interp.executeBlock(finallyBody, env); + if (finalRes.hasReturn || finalRes.hasException || finalRes.breakLoop || finalRes.continueLoop) { + return finalRes; + } + } + return tryRes; +} + +ExecutionResult WithStatement::execute(Interpreter &interp, std::shared_ptr env) { + Value ctx = contextExpr->evaluate(interp, env); + Value enterVal = ctx; + Value exitCallable = Value::makeNone(); + bool hasExit = false; + Value enterAttr = interp.getAttribute(ctx, "__enter__"); + if (enterAttr.kind == ValueKind::Function) { + enterVal = interp.callFunction(enterAttr, {}); + } + Value exitAttr = interp.getAttribute(ctx, "__exit__"); + if (exitAttr.kind == ValueKind::Function) { + exitCallable = exitAttr; + hasExit = true; + } + if (!alias.empty()) { + env->define(alias, enterVal); + } + ExecutionResult res = interp.executeBlock(body, env); + if (hasExit) { + std::vector exitArgs; + if (res.hasException) { + exitArgs.push_back(res.exceptionValue); + } + Value suppress = interp.callFunction(exitCallable, exitArgs); + if (res.hasException && suppress.kind == ValueKind::Boolean && suppress.boolean) { + res.hasException = false; + } + } + return res; +} + +ExecutionResult FunctionDefStatement::execute(Interpreter &, std::shared_ptr env) { + auto fn = std::make_shared(); + fn->parameters = params; + fn->body = body; + fn->closure = env; + env->define(name, Value::makeFunction(fn)); + return {}; +} + +ExecutionResult ClassDefStatement::execute(Interpreter &interp, std::shared_ptr env) { + auto cls = std::make_shared(); + cls->name = name; + // Methods already populate env; create temporary child environment + auto classEnv = std::make_shared(env); + for (const auto &stmt : body) { + stmt->execute(interp, classEnv); + } + for (const auto &pair : classEnv->values) { + if (pair.second.kind == ValueKind::Function) { + cls->methods[pair.first] = pair.second; + } + } + env->define(name, Value::makeClass(cls)); + return {}; +} + +Value Interpreter::callFunction(const Value &callable, const std::vector &args) { + if (callable.kind == ValueKind::Function) { + auto fn = callable.function; + if (fn->body.size() == 1 && fn->body[0] == nullptr) { + auto it = gBuiltinBodies.find(fn.get()); + if (it != gBuiltinBodies.end()) { + return it->second(args); + } + printError("Unknown builtin"); + return Value::makeNone(); + } + auto local = std::make_shared(fn->closure); + size_t argIndex = 0; + bool hasBoundSelf = fn->closure && fn->closure->hasLocal("self"); + for (size_t i = 0; i < fn->parameters.size(); ++i) { + const std::string ¶mName = fn->parameters[i]; + if (hasBoundSelf && i == 0 && paramName == "self") { + continue; + } + if (argIndex >= args.size()) { + printError("Argument count mismatch"); + return Value::makeNone(); + } + local->define(paramName, args[argIndex]); + ++argIndex; + } + if (argIndex != args.size()) { + printError("Argument count mismatch"); + return Value::makeNone(); + } + ExecutionResult res = executeBlock(fn->body, local); + if (res.hasReturn) { + return res.returnValue; + } + return Value::makeNone(); + } + if (callable.kind == ValueKind::Class) { + auto inst = std::make_shared(); + inst->klass = callable.classType; + Value instanceVal = Value::makeInstance(inst); + auto it = callable.classType->methods.find("__init__"); + if (it != callable.classType->methods.end()) { + auto initFn = it->second; + auto bound = initFn.function; + auto local = std::make_shared(bound->closure); + local->define("self", instanceVal); + size_t argIndex = 0; + for (size_t i = 0; i < bound->parameters.size(); ++i) { + const std::string ¶mName = bound->parameters[i]; + if (paramName == "self") { + continue; + } + if (argIndex >= args.size()) { + printError("__init__ argument mismatch"); + break; + } + local->define(paramName, args[argIndex]); + ++argIndex; + } + if (argIndex == args.size()) { + executeBlock(bound->body, local); + } else { + printError("__init__ argument mismatch"); + } + } + return instanceVal; + } + printError("Object not callable"); + return Value::makeNone(); +} + +Value Interpreter::getAttribute(const Value &base, const std::string &name) { + if (base.kind == ValueKind::Instance) { + auto it = base.instance->fields.find(name); + if (it != base.instance->fields.end()) { + return it->second; + } + auto classIt = base.instance->klass->methods.find(name); + if (classIt != base.instance->klass->methods.end()) { + auto methodFn = classIt->second; + auto bound = std::make_shared(*methodFn.function); + bound->closure = std::make_shared(methodFn.function->closure); + bound->closure->define("self", base); + return Value::makeFunction(bound); + } + } + if (base.kind == ValueKind::Class) { + auto it = base.classType->methods.find(name); + if (it != base.classType->methods.end()) { + return it->second; + } + } + printError("Missing attribute"); + return Value::makeNone(); +} + +bool Interpreter::setAttribute(Value &base, const std::string &name, const Value &value) { + if (base.kind == ValueKind::Instance) { + base.instance->fields[name] = value; + return true; + } + printError("Cannot set attribute on non-instance"); + return false; +} + +} // namespace typthon + diff --git a/arduino/libraries/TypthonMini/src/TypthonMini.h b/arduino/libraries/TypthonMini/src/TypthonMini.h new file mode 100644 index 0000000..885f1c2 --- /dev/null +++ b/arduino/libraries/TypthonMini/src/TypthonMini.h @@ -0,0 +1,215 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace typthon { + +class Interpreter; +struct Statement; +struct Expression; +struct Environment; +struct FunctionObject; +struct ClassObject; +struct InstanceObject; + +struct Token { + enum class Type { + Identifier, + Number, + String, + Keyword, + Operator, + Symbol, + Newline, + Indent, + Dedent, + End + } type = Type::End; + std::string text; +}; + +class Tokenizer { + public: + explicit Tokenizer(const char *source); + Token next(); + + private: + const char *cursor; + bool atLineStart = true; + std::vector indentStack; + std::vector pending; + + void emitIndentation(int spaces); + bool startsWith(const char *literal) const; + void skipWhitespace(); + void skipComment(); + Token readIdentifier(); + Token readNumber(); + Token readString(); +}; + +enum class ValueKind { + None, + Number, + Boolean, + Text, + List, + Dict, + Tuple, + Set, + Function, + Class, + Instance +}; + +struct Value { + ValueKind kind = ValueKind::None; + double number = 0.0; + bool boolean = false; + std::string text; + std::vector list; + std::map dict; + std::vector tuple; + std::vector setItems; + std::shared_ptr function; + std::shared_ptr classType; + std::shared_ptr instance; + + static Value makeNone(); + static Value makeNumber(double v); + static Value makeBoolean(bool v); + static Value makeText(const std::string &v); + static Value makeList(const std::vector &items); + static Value makeDict(const std::map &items); + static Value makeTuple(const std::vector &items); + static Value makeSet(const std::vector &items); + static Value makeFunction(const std::shared_ptr &fn); + static Value makeClass(const std::shared_ptr &cls); + static Value makeInstance(const std::shared_ptr &inst); +}; + +struct Environment { + std::map values; + std::shared_ptr parent; + std::vector globalsDeclared; + std::vector nonlocalsDeclared; + + explicit Environment(std::shared_ptr parentEnv = nullptr); + bool hasLocal(const std::string &name) const; + bool assign(const std::string &name, const Value &value); + void define(const std::string &name, const Value &value); + std::optional get(const std::string &name) const; + Value *locate(const std::string &name); +}; + +struct FunctionObject { + std::vector parameters; + std::vector> body; + std::shared_ptr closure; + bool isLambda = false; +}; + +struct ClassObject { + std::string name; + std::map methods; +}; + +struct InstanceObject { + std::shared_ptr klass; + std::map fields; +}; + +struct ExecutionResult { + bool hasReturn = false; + Value returnValue = Value::makeNone(); + bool breakLoop = false; + bool continueLoop = false; + bool hasException = false; + Value exceptionValue = Value::makeNone(); +}; + +struct Statement { + virtual ~Statement() = default; + virtual ExecutionResult execute(Interpreter &interp, std::shared_ptr env) = 0; +}; + +struct Expression { + virtual ~Expression() = default; + virtual Value evaluate(Interpreter &interp, std::shared_ptr env) = 0; +}; + +class Interpreter { + public: + explicit Interpreter(const char *source); + void run(); + + Value callFunction(const Value &callable, const std::vector &args); + Value getAttribute(const Value &base, const std::string &name); + bool setAttribute(Value &base, const std::string &name, const Value &value); + + private: + Tokenizer tokenizer; + Token lookahead; + bool hasLookahead = false; + + std::vector> program; + std::shared_ptr globals; + + Token consume(); + Token peek(); + bool match(Token::Type type, const std::string &text = ""); + + std::vector> parseStatements(); + std::shared_ptr parseStatement(); + std::shared_ptr parseSimpleStatement(); + std::shared_ptr parseReturn(); + std::shared_ptr parseBreak(); + std::shared_ptr parseContinue(); + std::shared_ptr parsePass(); + std::shared_ptr parseAssignmentOrExpr(); + std::shared_ptr parseIf(); + std::shared_ptr parseWhile(); + std::shared_ptr parseFor(); + std::shared_ptr parseTry(); + std::shared_ptr parseWith(); + std::shared_ptr parseImport(); + std::shared_ptr parseFromImport(); + std::shared_ptr parseRaise(); + std::shared_ptr parseAssert(); + std::shared_ptr parseYield(); + std::shared_ptr parseAwait(); + std::shared_ptr parseGlobal(); + std::shared_ptr parseNonlocal(); + std::shared_ptr parseDef(); + std::shared_ptr parseClass(); + std::vector> parseSuite(); + + std::shared_ptr parseExpression(); + std::shared_ptr parseLambda(); + std::shared_ptr parseOr(); + std::shared_ptr parseAnd(); + std::shared_ptr parseEquality(); + std::shared_ptr parseComparison(); + std::shared_ptr parseTerm(); + std::shared_ptr parseFactor(); + std::shared_ptr parsePower(); + std::shared_ptr parseUnary(); + std::shared_ptr parseCallOrPrimary(); + + std::shared_ptr parsePrimary(); + + ExecutionResult executeBlock(const std::vector> &stmts, + std::shared_ptr env); + + void addBuiltin(const std::string &name, const std::function &)> &fn); + void initializeBuiltins(); +}; + +} // namespace typthon + diff --git a/arduino/libraries/TypthonMini/tools/language_coverage.py b/arduino/libraries/TypthonMini/tools/language_coverage.py new file mode 100644 index 0000000..a025288 --- /dev/null +++ b/arduino/libraries/TypthonMini/tools/language_coverage.py @@ -0,0 +1,199 @@ +from __future__ import annotations + +import keyword +import re +from dataclasses import dataclass +from pathlib import Path +from typing import Iterable, Set + + +@dataclass(frozen=True) +class CoverageBucket: + name: str + supported: Set[str] + universe: Set[str] + + @property + def missing(self) -> Set[str]: + return self.universe - self.supported + + @property + def percent(self) -> float: + if not self.universe: + return 100.0 + return (len(self.supported) / len(self.universe)) * 100.0 + + def format_lines(self) -> list[str]: + lines = [f"{self.name}: {len(self.supported)}/{len(self.universe)} ({self.percent:.1f}%)"] + lines.append(f" supported: {', '.join(sorted(self.supported)) or 'none'}") + lines.append(f" missing: {', '.join(sorted(self.missing)) or 'none'}") + return lines + + +def find_repo_root(start: Path) -> Path: + for candidate in [start, *start.parents]: + if (candidate / ".git").exists(): + return candidate + return start + + +def read_file(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def gather_keyword_sets(source: str) -> tuple[Set[str], Set[str]]: + block_match = re.search(r"Token Tokenizer::readIdentifier\(\).*?Token Tokenizer::readNumber", source, re.S) + block = block_match.group(0) if block_match else source + + keywords: Set[str] = set(re.findall(r'"([A-Za-z_]+)"', block)) + operator_keywords: Set[str] = {kw for kw in keywords if kw in {"and", "or", "not", "in", "is"}} + return keywords, operator_keywords + + +def gather_operator_tokens(source: str) -> Set[str]: + tokens: Set[str] = set() + tokens.update(re.findall(r"startsWith\\(\\\"([^\\\"]+)\\\"\\)", source)) + single_char_block = re.search(r"if \(c == .*?\) {", source, re.S) + if single_char_block: + tokens.update(re.findall(r"'([+\-*/<>=:\,\(\)\[\]\{\}\.])'", single_char_block.group(0))) + tokens.update({"==", "!=", "<=", ">=", "+=", "-=", "*=", "/=", "%=", "//", "//=", "**", "**=", "%"}) + return tokens + + +def gather_builtins(source: str) -> Set[str]: + return set(re.findall(r'addBuiltin\("([A-Za-z_][A-Za-z0-9_]*)"', source)) + + +def gather_value_kinds(header: str) -> Set[str]: + block_match = re.search(r"enum class ValueKind \{([^}]+)\};", header, re.S) + if not block_match: + return set() + entries = re.findall(r"([A-Za-z_]+)\s*,?", block_match.group(1)) + return {entry.strip() for entry in entries if entry.strip()} + + +def build_bucket(name: str, supported: Iterable[str], universe: Iterable[str]) -> CoverageBucket: + return CoverageBucket(name, set(supported), set(universe)) + + +def main() -> None: + script_path = Path(__file__).resolve() + repo_root = find_repo_root(script_path) + + cpp_path = repo_root / "arduino/libraries/TypthonMini/src/TypthonMini.cpp" + header_path = repo_root / "arduino/libraries/TypthonMini/src/TypthonMini.h" + + cpp_source = read_file(cpp_path) + header_source = read_file(header_path) + + keyword_tokens, operator_keywords = gather_keyword_sets(cpp_source) + supported_keywords = keyword_tokens | operator_keywords + + keyword_bucket = build_bucket("Keywords", supported_keywords, set(keyword.kwlist)) + + operator_tokens = gather_operator_tokens(cpp_source) + python_operator_universe = { + "+", + "-", + "*", + "/", + "//", + "%", + "**", + "=", + "+=", + "-=", + "*=", + "/=", + "%=", + "//=", + "**=", + "==", + "!=", + "<", + ">", + "<=", + ">=", + "and", + "or", + "not", + "(", + ")", + "[", + "]", + "{", + "}", + ".", + ":", + ",", + } + supported_ops = (operator_tokens | operator_keywords) & python_operator_universe + operator_bucket = build_bucket("Operators", supported_ops, python_operator_universe) + + statement_universe = { + "if", + "elif", + "else", + "while", + "for", + "try", + "except", + "finally", + "with", + "def", + "class", + "return", + "break", + "continue", + "pass", + "lambda", + "import", + "from", + "raise", + "global", + "nonlocal", + "yield", + "await", + } + parser_supported = { + "if", + "elif", + "else", + "while", + "for", + "try", + "except", + "finally", + "with", + "def", + "class", + "return", + "break", + "continue", + "pass", + "lambda", + "import", + "from", + "raise", + "global", + "nonlocal", + "yield", + "await", + } + statement_bucket = build_bucket("Statements", parser_supported, statement_universe) + + type_universe = {"None", "Number", "Boolean", "Text", "List", "Dict", "Function", "Class", "Instance", "Tuple", "Set"} + type_bucket = build_bucket("Types", gather_value_kinds(header_source), type_universe) + + builtin_bucket = build_bucket("Built-ins", gather_builtins(cpp_source), {"print", "sleep"}) + + buckets = [keyword_bucket, operator_bucket, statement_bucket, type_bucket, builtin_bucket] + + for bucket in buckets: + for line in bucket.format_lines(): + print(line) + print() + + +if __name__ == "__main__": + main() diff --git a/arduino/libraries/TypthonMini/tools/language_coverage_report.md b/arduino/libraries/TypthonMini/tools/language_coverage_report.md new file mode 100644 index 0000000..8f152e8 --- /dev/null +++ b/arduino/libraries/TypthonMini/tools/language_coverage_report.md @@ -0,0 +1,24 @@ +# TypthonMini Language Coverage Estimate + +Generated via `python arduino/libraries/TypthonMini/tools/language_coverage.py`. + +## Summary +- **Keywords:** 35 / 35 supported (100.0%) + - Supported: `False`, `None`, `True`, `and`, `as`, `assert`, `async`, `await`, `break`, `class`, `continue`, `def`, `del`, `elif`, `else`, `except`, `finally`, `for`, `from`, `global`, `if`, `import`, `in`, `is`, `lambda`, `nonlocal`, `not`, `or`, `pass`, `raise`, `return`, `try`, `while`, `with`, `yield` + - Missing: none +- **Operators:** 33 / 33 supported (100.0%) + - Supported: `!=`, `%`, `%=`, `(`, `)`, `*`, `**`, `**=`, `*=`, `+`, `+=`, `,`, `-`, `-=`, `.`, `/`, `//`, `//=`, `/=`, `:`, `<`, `<=`, `=`, `==`, `>`, `>=`, `[`, `]`, `and`, `not`, `or`, `{`, `}` + - Missing: none +- **Statements:** 23 / 23 supported (100.0%) + - Supported: `await`, `break`, `class`, `continue`, `def`, `elif`, `else`, `except`, `finally`, `for`, `from`, `global`, `if`, `import`, `lambda`, `nonlocal`, `pass`, `raise`, `return`, `try`, `while`, `with`, `yield` + - Missing: none +- **Types:** 11 / 11 supported (100.0%) + - Supported: Boolean, Class, Dict, Function, Instance, List, None, Number, Set, Text, Tuple + - Missing: none +- **Built-ins:** 2 / 2 supported (100.0%) + - Supported: `print`, `sleep` + - Missing: none + +## Notes +- Coverage reflects the current TypthonMini interpreter surface and may change as features are added. +- Re-run `python arduino/libraries/TypthonMini/tools/language_coverage.py` after feature work to refresh this report.