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); // "
else
code = stoi(ent.substr(1)); // "
} 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;
}
}