JM Tech Power

jm-tech-power-official/JMLang

A programming language made by JM Tech Power

Public Uncategorized
Clone jm-tech-power-official/jmlang.jmproj
interp.cpp
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <stack>
#include <set>
#include <ctime>
#include <chrono>
#include <curl/curl.h>
#include <string>
#include <sstream>
#define INF 0x3f3f3f3f
using namespace std;
// download lib online
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {
    size_t written = fwrite(ptr, size, nmemb, stream);
    return written;
}
string unescape(const string& s) {
    string r;
    for (size_t i = 0; i < s.size(); ) {
        if (s[i] == '&') {
            size_t semi = s.find(';', i);
            if (semi != string::npos && semi - i <= 10) {
                string ent = s.substr(i + 1, semi - i - 1);   // 去掉 & 和 ;
                int code = -1;
                if (ent == "lt") code = '<';
                else if (ent == "gt") code = '>';
                else if (ent == "amp") code = '&';
                else if (ent == "quot") code = '"';
                else if (ent == "apos") code = '\'';
                else if (ent == "nbsp") code = ' ';
                else if (ent.size() > 1 && ent[0] == '#') {
                    try {
                        if (ent[1] == 'x' || ent[1] == 'X')
                            code = stoi(ent.substr(2), nullptr, 16);  // &#x22;
                        else
                            code = stoi(ent.substr(1));               // &#34;
                    } catch (...) { code = -1; }
                }
                if (code >= 0 && code < 128) {   // ASCII 直接塞进去
                    r += char(code);
                    i = semi + 1;
                    continue;
                }
            }
        }
        r += s[i++];
    }
    return r;
}
int download_lib_on_web(string lib) {
    CURL *curl;
    FILE *fp;
    CURLcode res;
    string surl = "https://www.jmtechpower.com/jm-tech-power-official/jmlang?tab=source&ref=refs/heads/main&path="+lib;
    const char *url = surl.c_str();
    const char *outfilename = lib.c_str();

    curl = curl_easy_init();
    if (curl) {
        fp = fopen(outfilename, "wb");
        if (!fp) {
            cerr << "Failed to open or create output file." << endl;
            return 1;
        }

        curl_easy_setopt(curl, CURLOPT_URL, url);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
        curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);

        res = curl_easy_perform(curl);

        curl_easy_cleanup(curl);
        fclose(fp);
        if (res != CURLE_OK) {
            cerr << "Failed to download " << outfilename << std::endl;
            remove(outfilename);
            exit(1);
        }
    }
    ifstream fin(lib);
    stringstream ss;
    ss << fin.rdbuf();
    string text = ss.str();

    const string L = "<pre class=\"jr-blob-code\">";
    const string R = "</pre>";

    size_t pos = 0;
    bool runed = 0;
    while ((pos = text.find(L, pos)) != string::npos) {
        runed = 1;
        size_t start = pos + L.size();
        size_t end = text.find(R, start);
        if (end == string::npos) break;
        string y = unescape(text.substr(start, end - start));
        if (!y.empty() && y[0] == '\n') y.erase(0, 1);
        ofstream fout(outfilename);
        fout.clear();
        fout << y << endl;
        pos = end + R.size();
    }
    if (!runed) {
        cerr << "No library named " << lib << " found." << endl;
        remove(outfilename);
        exit(1);
    }
    return 0;
}
///////////////////////////////////////////////
vector<string> loaded;
// Lexer
vector<string> tokenize(string str) {
    vector<string> tokens;
    string token;
    bool inStr = false;
    for (int i = 0; i < str.size(); i++) {
        if (str[i] == '"') {
            inStr = !inStr;
            token += str[i];
        } else if (str[i] == ' ' && !inStr) {
            if (token == "") continue;
            tokens.push_back(token);
            token = "";
        } else token += str[i];
    }
    if (token != "") tokens.push_back(token);
    return tokens;
}
// Parser
enum Operate {
    set,
    out,
    tick,
    times,
    in,
    lt,
    le,
    gt,
    ge,
    eq,
    ne,
    add,
    minus,
    multiply,
    divide,
    mod,
    var,
    land,
    lor,
    lnot,
    slen,
    sat,
    ord,
    chr,
};
struct Node {
    Operate op;
    string var;
    Node *left, *right;
};
struct Value {
    bool isStr = false;
    string s = "";
};
map<string, int> priority;
std::set<string> unaryOps = {"not","len","ord","chr"};
vector<map<string,Value>> frames;
map<string, int> labels;
stack<int> st;
map<int, int> labels2;
map<int, int> elseOf;
map<int, int> elseIf;
map<string, int> funcs;
stack<int> callStack;

void init() {
    priority["set"] = 0;
    priority["out"] = 0;
    priority["or"]  = 1;
    priority["and"] = 2;
    priority["not"] = 3;
    priority["<"]  = 4;  priority["<="] = 4;
    priority[">"]  = 4;  priority[">="] = 4;
    priority["=="] = 4;  priority["!="] = 4;
    priority["+"]  = 5;  priority["-"]  = 5;
    priority["*"]  = 6;  priority["/"]  = 6;
    priority["%"]  = 6;
    priority["len"] = 7;  priority["ord"] = 7;  priority["chr"] = 7;
    priority["at"]  = 8;
}
map<string,Value>& scope(string name) {
    if (name[0] == '@') return frames[0];
    return frames.back();
}
Node* parse(vector<string> tokens) {
    if (tokens.empty()) return nullptr;
    Node *root = new Node();
    int ti = -1;
    int mn = INF;
    for (int i = 0; i < tokens.size(); i++) {
        auto it = priority.find(tokens[i]);
        if (it != priority.end() && it->second <= mn) {
            mn = it->second;
            ti = i;
        }
    }
    if (ti != -1 && unaryOps.count(tokens[ti])) {
        for (int i = 0; i < tokens.size(); i++) {
            auto it = priority.find(tokens[i]);
            if (it != priority.end() && it->second == mn) { ti = i; break; }
        }
    }
    if (ti == -1) {
        if (tokens[0] == "-$in$-") {
            root->op = Operate::in;
            return root;
        } else if (tokens[0] == "-$time$-") {
            root->op = Operate::times;
            return root;
        } else if (tokens[0] == "-$tick$-") {
            root->op = Operate::tick;
            return root;
        }
        root->op = Operate::var;
        root->var = tokens[0];
        return root;
    }
    if (tokens[ti] == "set") {
        root->op = Operate::set;
    } else if (tokens[ti] == "out") {
        root->op = Operate::out;
    } else if (tokens[ti] == "+") {
        root->op = Operate::add;
    } else if (tokens[ti] == "-") {
        root->op = Operate::minus;
    } else if (tokens[ti] == "*") {
        root->op = Operate::multiply;
    } else if (tokens[ti] == "/") {
        root->op = Operate::divide;
    } else if (tokens[ti] == "<") {
        root->op = Operate::lt;
    } else if (tokens[ti] == "<=") {
        root->op = Operate::le;
    } else if (tokens[ti] == ">=") {
        root->op = Operate::ge;
    } else if (tokens[ti] == "==") {
        root->op = Operate::eq;
    } else if (tokens[ti] == "!=") {
        root->op = Operate::ne;
    } else if (tokens[ti] == ">") {
        root->op = Operate::gt;
    } else if (tokens[ti] == "%") {
        root->op = Operate::mod;
    } else if (tokens[ti] == "and") {
        root->op = Operate::land;
    } else if (tokens[ti] == "or") {
        root->op = Operate::lor;
    } else if (tokens[ti] == "not") {
        root->op = Operate::lnot;
    } else if (tokens[ti] == "len") {
        root->op = Operate::slen;
    } else if (tokens[ti] == "at") {
        root->op = Operate::sat;
    } else if (tokens[ti] == "ord") {
        root->op = Operate::ord;
    } else if (tokens[ti] == "chr") {
        root->op = Operate::chr;
    }
    vector<string> a;
    vector<string> b;
    for (int i = 0;i<tokens.size();i++) {
        string token = tokens[i];
        if (i < ti) {
            a.push_back(token);
        } else if (i > ti) {
            b.push_back(token);
        }
    }
    root->left = parse(a);
    root->right = parse(b);
    return root;
}
// Evaluator
int s2i(string s) {
    if (s[0] == '-') return -s2i(s.substr(1));
    int multi = 1;
    int ans = 0;
    for (int i = s.size()-1; i>=0; i--) {
        ans+=multi*(s[i]-'0');
        multi*=10;
    }
    return ans;
}
string i2s(int x) {
    if (x == 0) return "0";
    if (x < 0) return "-"+i2s(-x);
    string ans = "";
    while (x) {
        ans = char(x%10+'0') + ans;
        x /= 10;
    }
    return ans;
}
bool isNumber(string s) {
    if (s.empty() || s == "-") return false;
    for (int i = 0; i<s.size(); i++) {
        if (i == 0 && s[i] == '-') continue;
        if (s[i] < '0' || s[i] > '9') return false;

    }
    return true;
}
Value num(int x)    { return {false, i2s(x)}; }
Value str(string x) { return {true, x}; }
bool truthy(Value v) {
    if (v.isStr) return !v.s.empty();
    return s2i(v.s) != 0;
}
void doReturn(Value val, int &next) {
    frames.pop_back();
    frames.back()["ret"] = val;
    next = callStack.top();
    callStack.pop();
}
Value elevate(Node *root);
string resolveName(string raw) {
    int p = raw.find('<');
    if (p == -1) return raw;
    string arr = raw.substr(0, p);
    string idx = raw.substr(p+1,raw.size()-p-2);
    idx = elevate(parse(tokenize(idx))).s;
    return arr + "<" + idx + ">";
}
long long nowMs() {
    using namespace chrono;
    return duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
}
long long startMs = nowMs();
Value elevate(Node *root) {
    if (root->op == Operate::var) {
        string v = root->var;
        if (v[0] == '"') return str(v.substr(1, v.size() - 2));   // "abc" → 字符串
        if (v[0] == '$') {
            string n = v.substr(1);
            return scope(n)[resolveName(n)];                      // 变量:原样带着类型
        }
        if (isNumber(v)) return {false, v};                       // 123 → 数字
        return str(v);                                            // 其他裸词(比如标签名)
    } else if (root->op == Operate::out) {
        Value v = elevate(root->right);
        cout << v.s << endl;
        return v;
    } else if (root->op == Operate::in) {
        string w;
        cin >> w;
        return {!isNumber(w), w};      // 输入的像数字就当数字
    } else if (root->op == Operate::times) {
        return num((int)time(0));
    } else if (root->op == Operate::tick) {
        return num((int)(nowMs() - startMs));
    } else if (root->op == Operate::lnot) {
        return num(!truthy(elevate(root->right)));
    } else if (root->op == Operate::land) {
        if (!truthy(elevate(root->left))) return num(0);
        return num(truthy(elevate(root->right)));
    } else if (root->op == Operate::lor) {
        if (truthy(elevate(root->left))) return num(1);
        return num(truthy(elevate(root->right)));
    } else if (root->op == Operate::slen) {
        return num(elevate(root->right).s.size());               // len "abc" → 3
    } else if (root->op == Operate::ord) {
        string c = elevate(root->right).s;
        return num(c.empty() ? 0 : (unsigned char)c[0]);         // ord "A" → 65
    } else if (root->op == Operate::chr) {
        return str(string(1, (char)s2i(elevate(root->right).s))); // chr 65 → "A"
    } else if (root->op == Operate::sat) {
        string s = elevate(root->left).s;
        int i = s2i(elevate(root->right).s);
        if (i < 0 || i >= (int)s.size()) return str("");       // 越界给空字符串
        return str(string(1, s[i]));                             // "abc" at 1 → "b"
    }
    Value A = elevate(root->left);
    Value B = elevate(root->right);

    // 只要有一边是字符串,+ 就是拼接,== / != 就是比较文字
    if (A.isStr || B.isStr) {
        if (root->op == Operate::add) return str(A.s + B.s);
        if (root->op == Operate::eq)  return num(A.s == B.s);
        if (root->op == Operate::ne)  return num(A.s != B.s);
    }

    int a = s2i(A.s);
    int b = s2i(B.s);

    if (root->op == Operate::add) {
        return num(a+b);
    } else if (root->op == Operate::minus) {
        return num(a-b);
    } else if (root->op == Operate::multiply) {
        return num(a*b);
    } else if (root->op == Operate::divide) {
        return num(a/b);
    } else if (root->op == Operate::lt) {
        return num(a < b);
    } else if (root->op == Operate::le) {
        return num(a <= b);
    } else if (root->op == Operate::gt) {
        return num(a > b);
    } else if (root->op == Operate::ge) {
        return num(a >= b);
    } else if (root->op == Operate::eq) {
        return num(a == b);
    } else if (root->op == Operate::ne) {
        return num(a != b);
    } else if (root->op == Operate::mod) {
        return num(a % b);
    }
    return num(0);
}
void run(string line, int &next) {
    if (line[0] == ':') return;
    vector<string> tokens = tokenize(line);
    if (tokens.empty()) return;
    if (tokens[0][0] == '#') return;

    if (tokens[0] == "set") {
        string name = tokens[1];
        vector<string> rest(tokens.begin() + 2, tokens.end());
        scope(name)[resolveName(name)] = elevate(parse(rest));
        return;
    } else if (tokens[0] == "jump") {
        vector<string> rest(tokens.begin() + 1, tokens.end());
        string t = elevate(parse(rest)).s;
        if (labels.find(t) == labels.end()) {
            next = s2i(t) - 1;
        } else {
            next = labels[t];
        }
        return;
    } else if (tokens[0] == "jumpif") {
        vector<string> cond(tokens.begin() + 1, tokens.end() - 1);
        if (s2i(elevate(parse(cond)).s) == 0) return;
        string t = tokens.back();
        if (labels.find(t) == labels.end()) {
            next = s2i(t) - 1;
        } else {
            next = labels[t];
        }
        return;
    } else if (tokens[0] == "while") {
        int cur = next - 1;
        vector<string> cond(tokens.begin()+1,tokens.end());
        if (s2i(elevate(parse(cond)).s) == 0) {
            next = labels2[cur] + 1;
        }
        return;
    } else if (tokens[0] == "if") {
        int cur = next - 1;
        vector<string> cond(tokens.begin()+1,tokens.end());
        if (s2i(elevate(parse(cond)).s) == 0) {
            if (elseOf.count(cur)) next = elseOf[cur] + 1;
            else next = labels2[cur] + 1;
        }
        return;
    } else if (tokens[0] == "else") {
        int cur = next - 1;
        next = labels2[elseIf[cur]] + 1;
        return;
    } else if (tokens[0] == "func") {
        int cur = next - 1;
        next = labels2[cur] + 1;
        return;
    } else if (tokens[0] == "end") {
        int cur = next - 1;
        string opener = tokenize(loaded[labels2[cur]])[0];
        if (opener == "if") return;
        if (opener == "func") {
            doReturn(num(0),next);
            return;
        }
        next = labels2[cur];
        return;
    } else if (tokens[0] == "call") {
        string name = tokens[1];
        if (!funcs.count(name)) {
            cerr << "Function \"" << name << "\" not found." << endl;
            exit(1);
        }

        vector<Value> argVals;
        vector<string> cur;
        for (int i = 2; i < tokens.size(); i++) {
            if (tokens[i] == ",") {
                argVals.push_back(elevate(parse(cur)));
                cur.clear();
            } else {
                cur.push_back(tokens[i]);
            }
        }
        if (!cur.empty()) argVals.push_back(elevate(parse(cur)));

        frames.push_back({});

        vector<string> header = tokenize(loaded[funcs[name]]);
        for (int i = 0; i < argVals.size() && i + 2 < header.size(); i++) {
            frames.back()[header[i + 2]] = argVals[i];
        }

        callStack.push(next);
        next = funcs[name] + 1;
        return;
    } else if (tokens[0] == "ret") {
        vector<string> retExpr(tokens.begin() + 1, tokens.end());
        Value val = num(0);
        if (!retExpr.empty()) val = elevate(parse(retExpr));
        doReturn(val, next);
        return;
    }

    Node* root = parse(tokens);
    if (root) elevate(root);
}
std::set<string> imported;              // 记录已经导入过的文件
void loadFile(string path) {
    if (imported.count(path)) return;   // 导入过就跳过
    imported.insert(path);

    ifstream fin(path.c_str());
    if (!fin) {
        cerr << "打不开文件: " << path << endl;
        exit(1);
    }
    string line;
    while (getline(fin, line)) {
        vector<string> t = tokenize(line);
        if (!t.empty() && t[0] == "import") {
            string lib = t[1].substr(1, t[1].size() - 2);
            if (lib.substr(0,5) == "jm://") {
                lib = lib.substr(5);
                if (!imported.count(lib)) download_lib_on_web(lib);
            } else {
                ifstream fin2(lib.c_str());
                if (!fin2) {
                    download_lib_on_web(lib);
                }
            }
            loadFile(lib);
            continue;
        }
        loaded.push_back(line);
    }
}
int main(int argc, char* argv[]) {
    init();
    frames.push_back({});
    string file;
    if (argc < 2) {
        cin >> file;
    } else {
        file = argv[1];
    }
    ifstream fin(file.c_str());
    if (!fin) {
        cerr << "Can't open file: " << file << endl;
        return 1;
    }

    string line;
    loadFile(file);

    for (int lineCo = 0; lineCo < loaded.size(); lineCo++) {
        vector<string> t = tokenize(loaded[lineCo]);
        string first = t.empty() ? "" : t[0];

        if (first != "" && first[0] == ':') {
            labels[first.substr(1)] = lineCo;
        }
        if (first == "while" || first == "if") {
            st.push(lineCo);
        }
        if (first == "func") {
            st.push(lineCo);
            funcs[t[1]] = lineCo;
        }
        if (first == "else") {
            int ifLine = st.top();
            elseOf[ifLine] = lineCo;
            elseIf[lineCo] = ifLine;
        }
        if (first == "end") {
            int tp = st.top();
            st.pop();
            labels2[tp] = lineCo;
            labels2[lineCo] = tp;
        }
    }

    // run
    int pc = 0;
    while (pc < loaded.size()) {
        int next = pc + 1;
        run(loaded[pc], next);
        pc = next;
    }
}