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> tokenizeRaw(string str) {
vector<string> tokens;
string token;
bool inStr = false;
for (int i = 0; i < str.size(); i++) {
if (inStr && str[i] == '\\' && i + 1 < str.size()) {
token += str[i];
token += str[i + 1];
i++;
} else 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
string unescapeStr(const string& s) {
string r;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '\\' && i + 1 < s.size()) {
i++;
if (s[i] == 'n') r += '\n';
else if (s[i] == 't') r += '\t';
else r += s[i];
} else {
r += s[i];
}
}
return r;
}
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,
fileread,
filelines,
fileline,
fileexists,
};
struct Node {
Operate op;
string var;
Node *left, *right;
};
struct Value {
bool isStr = false;
string s = "";
int obj = 0;
int ptr = 0;
int n = 0;
};
struct PtrInfo {
int obj = 0;
int depth = 0;
int fid = 0;
string arr;
bool isElem = false;
int idx = 0;
};
map<string, int> priority;
std::set<string> unaryOps = {"not","len","ord","chr","read","lines","exists"};
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;
vector<int> loopSt;
map<int, int> loopOf;
map<int, map<string, Value>> heap;
int objCount = 0;
map<string, vector<string>> structs;
std::set<string> classes;
map<string, string> parentOf;
vector<PtrInfo> ptrs(1);
int frameSerial = 0;
vector<string> srcFile; // 每一行来自哪个文件
vector<int> srcLine; // 是那个文件的第几行
int curLine = -1; // 现在正在跑 loaded 里的第几行
void die(string msg, int at = -1) {
if (at == -1) at = curLine;
if (at >= 0 && at < (int)loaded.size()) {
cerr << "[" << srcFile[at] << " Line " << srcLine[at] << "] " << msg << endl;
cerr << " > " << loaded[at] << endl;
} else {
cerr << msg << endl;
}
exit(1);
}
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;
priority["read"] = 7; priority["lines"] = 7; priority["exists"] = 7;
priority["line"] = 8;
}
map<string,Value>& scope(const string& name) {
if (name[0] == '@') return frames[0];
return frames.back();
}
Node* parseRaw(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;
}
if (tokens.size() > 1) die("Can't understand: " + tokens[1]);
root->op = Operate::var;
root->var = tokens[0];
if (root->var[0] == '"')
root->var = "\"" + unescapeStr(root->var.substr(1, root->var.size() - 2)) + "\"";
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;
} else if (tokens[ti] == "read") {
root->op = Operate::fileread;
} else if (tokens[ti] == "lines") {
root->op = Operate::filelines;
} else if (tokens[ti] == "line") {
root->op = Operate::fileline;
} else if (tokens[ti] == "exists") {
root->op = Operate::fileexists;
}
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 = parseRaw(a);
root->right = parseRaw(b);
return root;
}
// 缓存:同样的一行只拆一次、同样的表达式只建一次树
map<string, vector<string>> tokCache;
const vector<string>& tokenize(const string& str) {
auto it = tokCache.find(str);
if (it != tokCache.end()) return it->second;
return tokCache[str] = tokenizeRaw(str);
}
map<string, Node*> parseCache;
Node* parse(const vector<string>& tokens) {
string key;
for (auto &t : tokens) { key += t; key += '\x01'; }
auto it = parseCache.find(key);
if (it != parseCache.end()) return it->second;
return parseCache[key] = parseRaw(tokens);
}
// Evaluator
int s2i(const 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(const 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) { Value v; v.n = x; return v; }
// 拿到"文字形式":数字现场转成字符串,其他的直接用 s
string txt(const Value &v) {
if (!v.isStr && !v.obj && !v.ptr) return i2s(v.n);
return v.s;
}
// 拿到"数字形式"
int toInt(const Value &v) {
if (v.isStr) return s2i(v.s);
return v.n;
}
Value str(string x) { return {true, x}; }
bool truthy(Value v) {
if (v.obj || v.ptr) return true;
if (v.isStr) return !v.s.empty();
return v.n != 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(const 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 = txt(elevate(parse(tokenize(idx))));
return arr + "<" + idx + ">";
}
// "p.pos.x" → ["p","pos","x"](<> 里面的点不拆)
vector<string> splitDots(const string& s) {
vector<string> parts;
string cur;
int depth = 0;
for (char c : s) {
if (c == '<') depth++;
if (c == '>') depth--;
if (c == '.' && depth == 0) { parts.push_back(cur); cur = ""; }
else cur += c;
}
parts.push_back(cur);
return parts;
}
// raw 是 "对象.字段" 就返回那个字段的位置,不是就返回 nullptr
Value* fieldRef(const string& raw, bool create) {
vector<string> parts = splitDots(raw);
if (parts.size() < 2) return nullptr;
string base = resolveName(parts[0]);
auto &sc = scope(parts[0]);
if (!sc.count(base) || sc[base].obj == 0) return nullptr; // 不是对象,当普通名字
Value *v = &sc[base];
for (int i = 1; i < parts.size(); i++) {
if (v->obj == 0) die("Not an object: " + parts[i - 1]);
auto &fields = heap[v->obj];
string f = resolveName(parts[i]);
if (!create && !fields.count(f)) die("No field: " + f);
v = &fields[f];
}
return v;
}
string ptrKey(PtrInfo &pi) {
return pi.isElem ? pi.arr + "<" + i2s(pi.idx) + ">" : pi.arr;
}
Value newPtr(PtrInfo pi) {
ptrs.push_back(pi);
Value v;
v.ptr = ptrs.size() - 1;
v.s = "<ptr " + ptrKey(pi) + ">";
return v;
}
Value makePtr(string raw) {
PtrInfo pi;
string key;
vector<string> parts = splitDots(raw);
if (parts.size() >= 2) { // 可能是对象字段
string base = resolveName(parts[0]);
auto &sc = scope(parts[0]);
if (sc.count(base) && sc[base].obj) {
Value *v = &sc[base];
for (int i = 1; i + 1 < parts.size(); i++) {
if (!v->obj) die("Not an object: " + parts[i - 1]);
v = &heap[v->obj][resolveName(parts[i])];
}
if (!v->obj) die("Not an object: " + parts[parts.size() - 2]);
pi.obj = v->obj;
key = resolveName(parts.back());
}
}
if (!pi.obj) { // 普通变量
key = resolveName(raw);
pi.depth = (raw[0] == '@') ? 0 : frames.size() - 1;
pi.fid = frames[pi.depth]["__fid"].n;
}
int p = key.find('<');
if (p != -1 && isNumber(key.substr(p + 1, key.size() - p - 2))) {
pi.isElem = true;
pi.arr = key.substr(0, p);
pi.idx = s2i(key.substr(p + 1, key.size() - p - 2));
} else {
pi.arr = key;
}
return newPtr(pi);
}
// 指针指向的那个"家"(某个栈帧或某个对象)
map<string, Value>& ptrHome(PtrInfo &pi) {
if (pi.obj) return heap[pi.obj];
if (pi.depth >= frames.size() || frames[pi.depth]["__fid"].n != pi.fid)
die("Dangling pointer: " + pi.arr + " is gone (its function already returned)");
return frames[pi.depth];
}
Value& derefRef(Value p) { // set *p ...
if (!p.ptr) die("Not a pointer: " + txt(p));
PtrInfo &pi = ptrs[p.ptr];
return ptrHome(pi)[ptrKey(pi)];
}
Value derefGet(Value p) { // $*p
if (!p.ptr) die("Not a pointer: " + txt(p));
PtrInfo &pi = ptrs[p.ptr];
auto &home = ptrHome(pi);
string k = ptrKey(pi);
if (!home.count(k)) {
if (pi.isElem) return num(0);
die("Pointer points to unset variable: " + k);
}
return home[k];
}
bool samePtr(PtrInfo &a, PtrInfo &b) {
return a.obj == b.obj && a.depth == b.depth && a.fid == b.fid && ptrKey(a) == ptrKey(b);
}
// 读变量($x / $a<1> / $p.x)
Value getVar(const string& raw) {
if (raw.find_first_of(".<*") == string::npos) {
auto &sc = scope(raw);
auto it = sc.find(raw);
if (it == sc.end()) die("Variable not set: $" + raw);
return it->second;
}
if (raw[0] == '*') return derefGet(getVar(raw.substr(1)));
Value *f = fieldRef(raw, false);
if (f) return *f;
string full = resolveName(raw);
auto &sc = scope(raw);
if (!sc.count(full)) {
if (full.find('<') != string::npos) return num(0);
die("Variable not set: $" + full);
}
return sc[full];
}
// 写变量的位置(set x / set a<1> / set p.x)
Value& setRef(const string& raw) {
if (raw.find_first_of(".<*") == string::npos) return scope(raw)[raw];
if (raw[0] == '*') return derefRef(getVar(raw.substr(1)));
Value *f = fieldRef(raw, true);
if (f) return *f;
return scope(raw)[resolveName(raw)];
}
Value makeObj(string cls) {
int id = ++objCount;
heap[id]["__class"] = str(cls);
Value v;
v.obj = id;
v.s = "<" + cls + "#" + i2s(id) + ">";
return v;
}
vector<Value> evalArgs(vector<string> &tokens, int from) {
vector<Value> args;
vector<string> cur;
for (int i = from; i < tokens.size(); i++) {
if (tokens[i] == ",") { args.push_back(elevate(parse(cur))); cur.clear(); }
else cur.push_back(tokens[i]);
}
if (!cur.empty()) args.push_back(elevate(parse(cur)));
return args;
}
string findMethod(string cls, string m) {
while (cls != "") {
if (funcs.count(cls + "." + m)) return cls + "." + m;
cls = parentOf.count(cls) ? parentOf[cls] : "";
}
return "";
}
void enterFunc(string fname, vector<Value> args, Value self, int &next) {
frames.push_back({});
frames.back()["__fid"] = num(++frameSerial);
if (self.obj) frames.back()["self"] = self;
vector<string> header = tokenize(loaded[funcs[fname]]);
for (int i = 0; i < args.size() && i + 2 < header.size(); i++) {
frames.back()[header[i + 2]] = args[i];
}
callStack.push(next);
next = funcs[fname] + 1;
}
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 == nullptr) die("Code not complete");
if (root->op == Operate::var) {
string v = root->var;
if (v[0] == '"') return str(v.substr(1, v.size() - 2)); // "abc" → 字符串
if (v[0] == '$') {
return getVar(v.substr(1));
}
if (v[0] == '&') return makePtr(v.substr(1));
if (isNumber(v)) return num(s2i(v)); // 123 → 数字
return str(v); // 其他裸词(比如标签名)
} else if (root->op == Operate::out) {
Value v = elevate(root->right);
cout << txt(v);
return v;
} else if (root->op == Operate::in) {
string w;
cin >> w;
return isNumber(w) ? num(s2i(w)) : str(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(txt(elevate(root->right)).size()); // len "abc" → 3
} else if (root->op == Operate::ord) {
string c = txt(elevate(root->right));
return num(c.empty() ? 0 : (unsigned char)c[0]); // ord "A" → 65
} else if (root->op == Operate::chr) {
return str(string(1, (char)toInt(elevate(root->right)))); // chr 65 → "A"
} else if (root->op == Operate::sat) {
string s = txt(elevate(root->left));
int i = toInt(elevate(root->right));
if (i < 0 || i >= (int)s.size()) return str(""); // 越界给空字符串
return str(string(1, s[i])); // "abc" at 1 → "b"
} else if (root->op == Operate::fileexists) {
ifstream f(txt(elevate(root->right)));
return num(f ? 1 : 0);
} else if (root->op == Operate::fileread) {
ifstream f(txt(elevate(root->right)));
stringstream ss;
ss << f.rdbuf();
return str(ss.str());
} else if (root->op == Operate::filelines) {
ifstream f(txt(elevate(root->right)));
string l;
int n = 0;
while (getline(f, l)) n++;
return num(n);
} else if (root->op == Operate::fileline) {
ifstream f(txt(elevate(root->left)));
int want = toInt(elevate(root->right));
string l;
for (int i = 0; getline(f, l); i++) {
if (i == want) return isNumber(l) ? num(s2i(l)) : str(l); // 像数字就当数字
}
return str("");
}
Value A = elevate(root->left);
Value B = elevate(root->right);
if (A.ptr || B.ptr) {
// 指针 + 整数:往后挪几个数组元素
if (A.ptr && !B.ptr && !B.isStr && !B.obj &&
(root->op == Operate::add || root->op == Operate::minus)) {
PtrInfo pi = ptrs[A.ptr];
if (!pi.isElem) die("Pointer math only works on array elements");
pi.idx += (root->op == Operate::add ? 1 : -1) * toInt(B);
return newPtr(pi);
}
if (root->op == Operate::eq || root->op == Operate::ne) {
bool same = A.ptr && B.ptr && samePtr(ptrs[A.ptr], ptrs[B.ptr]);
return num(root->op == Operate::eq ? same : !same);
}
if (root->op == Operate::add && (A.isStr || B.isStr)) return str(txt(A) + txt(B));
die("Pointer can't do this math");
}
if (A.obj || B.obj) {
if (root->op == Operate::eq) return num(txt(A) == txt(B));
if (root->op == Operate::ne) return num(txt(A) != txt(B));
if (root->op == Operate::add && (A.isStr || B.isStr)) return str(txt(A) + txt(B));
die("Object can't do math");
}
// 只要有一边是字符串,+ 就是拼接,== / != 就是比较文字
if (A.isStr || B.isStr) {
if (root->op == Operate::add) return str(txt(A) + txt(B));
if (root->op == Operate::eq) return num(txt(A) == txt(B));
if (root->op == Operate::ne) return num(txt(A) != txt(B));
}
if (A.isStr || B.isStr) die("String \"" + (A.isStr ? A.s : B.s) + "\" can't do math");
int a = A.n;
int b = B.n;
if ((root->op == Operate::divide || root->op == Operate::mod) && b == 0) die("Divide by 0");
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(const string& line, int &next) {
int cur = next-1;
curLine = cur;
if (line[0] == ':') return;
vector<string> tokens = tokenize(line);
if (tokens.empty()) return;
if (tokens[0][0] == '#') return;
if (tokens[0] == "set") {
if (tokens.size() < 3) die("usage: set variable value");
string name = tokens[1];
vector<string> rest(tokens.begin() + 2, tokens.end());
setRef(name) = elevate(parse(rest));
return;
} else if (tokens[0] == "jump") {
vector<string> rest(tokens.begin() + 1, tokens.end());
string t = txt(elevate(parse(rest)));
if (labels.find(t) == labels.end()) {
if (!isNumber(t)) die("Label not found: " + t);
next = s2i(t) - 1;
} else {
next = labels[t];
}
return;
} else if (tokens[0] == "jumpif") {
vector<string> cond(tokens.begin() + 1, tokens.end() - 1);
if (toInt(elevate(parse(cond))) == 0) return;
string t = tokens.back();
if (labels.find(t) == labels.end()) {
if (!isNumber(t)) die("Label not found: " + t);
next = s2i(t) - 1;
} else {
next = labels[t];
}
return;
} else if (tokens[0] == "while") {
vector<string> cond(tokens.begin()+1,tokens.end());
if (toInt(elevate(parse(cond))) == 0) {
next = labels2[cur] + 1;
}
return;
} else if (tokens[0] == "if") {
vector<string> cond(tokens.begin()+1,tokens.end());
if (toInt(elevate(parse(cond))) == 0) {
if (elseOf.count(cur)) next = elseOf[cur] + 1;
else next = labels2[cur] + 1;
}
return;
} else if (tokens[0] == "else") {
next = labels2[elseIf[cur]] + 1;
return;
} else if (tokens[0] == "break") {
next = labels2[loopOf[cur]] + 1;
return;
} else if (tokens[0] == "continue") {
next = loopOf[cur];
return;
} else if (tokens[0] == "struct") {
return;
} else if (tokens[0] == "class") {
next = labels2[cur] + 1; // 跳过整个类
return;
} else if (tokens[0] == "new") {
if (tokens.size() < 3) die("usage: new variable Type args");
string target = tokens[1], cls = tokens[2];
vector<Value> args = evalArgs(tokens, 3);
if (structs.count(cls)) {
Value o = makeObj(cls);
vector<string> &fs = structs[cls];
for (int i = 0; i < fs.size(); i++)
heap[o.obj][fs[i]] = i < args.size() ? args[i] : num(0);
setRef(target) = o;
return;
}
if (!classes.count(cls)) die("Type not found: " + cls);
Value o = makeObj(cls);
setRef(target) = o;
string init = findMethod(cls, "init"); // 有 init 就自动调用
if (init != "") enterFunc(init, args, o, next);
return;
} else if (tokens[0] == "func") {
next = labels2[cur] + 1;
return;
} else if (tokens[0] == "end") {
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];
vector<Value> argVals = evalArgs(tokens, 2);
if (name[0] == '$') { // 方法调用:call $p.move 1 , 2
vector<string> parts = splitDots(name.substr(1));
if (parts.size() < 2) die("usage: call $object.method args");
string m = parts.back();
parts.pop_back();
string objName = parts[0];
for (int i = 1; i < parts.size(); i++) objName += "." + parts[i];
Value self = getVar(objName);
if (!self.obj) die("Not an object: " + objName);
string cls = heap[self.obj]["__class"].s;
string fname = findMethod(cls, m);
if (fname == "") die(cls + " has no method: " + m);
enterFunc(fname, argVals, self, next);
return;
}
if (!funcs.count(name)) die("Function not found: " + name);
enterFunc(name, argVals, Value(), next);
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;
} else if (tokens[0] == "write" || tokens[0] == "append") {
vector<string> pathT, valT;
bool afterComma = false;
for (int i = 1; i < tokens.size(); i++) {
if (!afterComma && tokens[i] == ",") { afterComma = true; continue; }
if (afterComma) valT.push_back(tokens[i]);
else pathT.push_back(tokens[i]);
}
string path = txt(elevate(parse(pathT)));
string val = valT.empty() ? "" : txt(elevate(parse(valT)));
ofstream f;
if (tokens[0] == "write") f.open(path);
else f.open(path, ios::app);
if (!f) {
die("Can't write file: " + path);
}
f << val << endl;
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;
int lineNo = 0;
while (getline(fin, line)) {
lineNo++;
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);
srcFile.push_back(path);
srcLine.push_back(lineNo);
}
}
int main(int argc, char* argv[]) {
init();
frames.push_back({});
frames.back()["__fid"] = num(++frameSerial);
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);
string curClass = "";
int classLine = -1;
for (int lineCo = 0; lineCo < loaded.size(); lineCo++) {
vector<string> t = tokenize(loaded[lineCo]);
string first = t.empty() ? "" : t[0];
if (!first.empty() && first[0] == ':') {
labels[first.substr(1)] = lineCo;
}
if (first == "while" || first == "if") {
st.push(lineCo);
}
if (first == "while") loopSt.push_back(lineCo);
if (first == "struct") {
if (t.size() < 2) die("usage: struct Name field1 field2 ...", lineCo);
structs[t[1]] = vector<string>(t.begin() + 2, t.end());
}
if (first == "class") {
if (curClass != "") die("class inside class", lineCo);
st.push(lineCo);
curClass = t[1];
classLine = lineCo;
classes.insert(t[1]);
if (t.size() >= 4 && t[2] == "extends") parentOf[t[1]] = t[3];
}
if (first == "func") {
st.push(lineCo);
if (curClass == "") funcs[t[1]] = lineCo;
else funcs[curClass + "." + t[1]] = lineCo; // 方法名:类名.方法名
loopSt.push_back(-1);
}
if (first == "break" || first == "continue") {
if (loopSt.empty() || loopSt.back() == -1) die(first + " not in while", lineCo);
loopOf[lineCo] = loopSt.back();
}
if (first == "else") {
if (st.empty() || tokenize(loaded[st.top()])[0] != "if") die("else without if", lineCo);
int ifLine = st.top();
elseOf[ifLine] = lineCo;
elseIf[lineCo] = ifLine;
}
if (first == "end") {
if (st.empty()) die("Extra end", lineCo);
int tp = st.top();
st.pop();
string opener = tokenize(loaded[tp])[0];
if (opener == "while" || opener == "func") loopSt.pop_back();
labels2[tp] = lineCo;
labels2[lineCo] = tp;
if (tp == classLine) { curClass = ""; classLine = -1; }
}
}
if (!st.empty()) die("Block has no end", st.top());
// run
int pc = 0;
while (pc < loaded.size()) {
int next = pc + 1;
run(loaded[pc], next);
pc = next;
}
}