Skip to content

Commit 322e85e

Browse files
committed
feat: 升级 HIC v0.02 并归档 v0.01 至 old/
- 引擎 hic.js:中文标识符、in 分级展示(t/s/b/link/p)、四则运算与优先级、链接渲染 - 同步更新官网、IDE 界面与学习中心,新增 v0.02 特性教程 - 13 章 .hc 练习文件升级至 v0.02 并补充新语法示例 - 归档 v0.01 至 old/v0.01/,页面顶部加入返回最新版提示
1 parent ea4d004 commit 322e85e

48 files changed

Lines changed: 3461 additions & 94 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,17 @@ HiCode(简称 **HC**)的诞生源于一个朴素的愿望:**让编程真
2828
-**开源可用**:完全开放源代码,自由使用与二次开发
2929
-**多端开放**:网页、安卓、Windows 三大平台同时支持开发与运行
3030

31+
## 当前版本
32+
33+
- **v0.02(网页端在线开发)**:支持 `it` 变量(含中文名)、`in` 分级展示(`t / s / b / link / p`)、`if / orif / noif` 条件、四则运算表达式、`to / fr` 页面跳转,可实时转译为网页并导出 `.hc` / `.html` / `.zip`
34+
- **历史版本**:v0.01 已归档至仓库 `old/v0.01/`,用户仍可访问旧版预览版。
35+
3136
## 官网
3237

3338
- 官网地址:**[https://HiCodeHC.github.io](https://HiCodeHC.github.io)**
39+
- 网页端体验:**[https://HiCodeHC.github.io/ide/](https://HiCodeHC.github.io/ide/)**
40+
- 学习中心:**[https://HiCodeHC.github.io/learn/](https://HiCodeHC.github.io/learn/)**
41+
- 历史版本 v0.01:**[https://HiCodeHC.github.io/old/v0.01/](https://HiCodeHC.github.io/old/v0.01/)**
3442

3543
## 联系与建议
3644

ide/css/style.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* ============================================================
2-
* HC v0.01 在线 IDE 样式 —— 浅色液态玻璃风格
2+
* HC v0.02 在线 IDE 样式 —— 浅色液态玻璃风格
33
* ============================================================ */
44
:root {
55
--bg: #f3eee6;

ide/index.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6-
<title>HiCode · 在线开发环境 v0.01</title>
6+
<title>HiCode · 在线开发环境 v0.02</title>
77
<link rel="icon" href="../assets/hicode-logo.jpg" type="image/jpeg" />
88
<link rel="stylesheet" href="css/style.css" />
99
</head>
@@ -13,7 +13,7 @@
1313
<div class="brand" id="btnHome" title="返回首页">
1414
<span class="logo-dot"></span>
1515
<span class="brand-name">HiCode</span>
16-
<span class="ver">v0.01 · 网页端</span>
16+
<span class="ver">v0.02 · 网页端</span>
1717
</div>
1818
<div class="crumb" id="crumb" title="当前文件"></div>
1919
<div class="actions">
@@ -62,10 +62,10 @@
6262
<section class="pane on" id="pane-editor">
6363
<div class="editor-head">
6464
<span class="hint">页面自带 HTML 骨架,无需编写 <code>&lt;html&gt;</code> 等标签</span>
65-
<span class="shortcut">支持:it 变量 · in t 文字 / in p 图片 · if/orif/noif 判断 · to/fr 导向</span>
65+
<span class="shortcut">支持:it 变量(含中文名) · in t/s/b/p/link 展示 · if/orif/noif 判断 · 四则运算 · to/fr 导向</span>
6666
</div>
6767
<textarea id="code" class="codearea" spellcheck="false"
68-
placeholder="在这里编写 HIC 代码…&#10;&#10;it 标题 t Hello HiCode&#10;标题 in t"
68+
placeholder="在这里编写 HIC 代码…&#10;&#10;it 标题 t Hello HiCode&#10;标题 in t&#10;it 分数 int 85&#10;if 分数 &gt;= 60:&#10; 标题 in s"
6969
></textarea>
7070
<div class="statusbar" id="statusbar">未打开页面</div>
7171
</section>
@@ -81,9 +81,9 @@
8181

8282
<section class="pane" id="pane-vars">
8383
<div class="var-help">
84-
<p><b>it 变量</b>:默认普通变量(前端不显示)。<code>t</code> 显示文字<code>p</code> 存放图片</p>
85-
<p><code>变量名 in t</code> 居中显示文字;<code>变量名 in p</code> 显示图片(本版坐标居中)</p>
86-
<p>点击下方图片条目即可上传 / 更换 / 删除图片。</p>
84+
<p><b>it 变量</b>:默认普通变量(前端不显示)。<code>t</code> 文字<code>p</code> 图片;变量名支持<b>中文</b></p>
85+
<p><code>变量名 in t</code> 标题 · <code>in s</code> 副标题 · <code>in b</code> 正文 · <code>in link</code> 链接 · <code>in p</code> 图片</p>
86+
<p>条件支持 <b>四则运算</b><code>+ - * / %</code>)与括号。点击下方图片条目即可上传 / 更换 / 删除图片。</p>
8787
</div>
8888
<div id="varList" class="var-list"></div>
8989
<div class="var-empty" id="varEmpty">当前页面没有声明变量</div>
@@ -99,7 +99,7 @@
9999
<div class="home-logo"><span class="logo-dot lg"></span></div>
100100
<h1>HiCode <span class="dim">· HIC 语言在线开发</span></h1>
101101
<p class="home-sub">自研 HIC 语言一键转译为 HTML,零基础也能搭建网页项目。</p>
102-
<p class="home-sub small">v0.01 为代码开发版本,支持项目 / 页面管理、条件、导向、实时转译与导出。</p>
102+
<p class="home-sub small">v0.02 为代码开发版本,新增中文变量名、四则运算、展示样式分级与链接;支持项目 / 页面管理、条件、导向、实时转译与导出。</p>
103103
<div class="home-actions">
104104
<button id="homeNew" class="btn solid big">新建项目</button>
105105
<button id="homeImport" class="btn ghost big">导入项目 .hc</button>

ide/js/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* ============================================================
2-
* HC v0.01 在线 IDE —— 界面逻辑 app.js
2+
* HC v0.02 在线 IDE —— 界面逻辑 app.js
33
* 依赖:Store (store.js) + HC (hic.js)
44
* 职责:首页引导、项目/页面 CRUD、代码编辑、实时转译预览、
55
* 变量面板(含 p 图片上传)、导出菜单。

ide/js/hic.js

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* ============================================================
2-
* HC v0.01 —— HIC 语言引擎
2+
* HC v0.02 —— HIC 语言引擎
33
* 负责:HIC 词法/语法解析、HIC→HTML 转译、轻量 ZIP 写入、下载
44
* 说明:本文件为纯逻辑,不依赖 DOM(localStorage/document),
55
* 唯一例外是 download()(仅浏览器调用);可用 Node 单元测试。
@@ -11,15 +11,19 @@
1111
})(typeof self !== 'undefined' ? self : null, function () {
1212
"use strict";
1313

14-
const APP = { version: "v0.01", name: "HiCode", lang: "HIC" };
14+
const APP = { version: "v0.02", name: "HiCode", lang: "HIC" };
1515

1616
/* ---------------- 工具 ---------------- */
1717
function esc(s) {
1818
return String(s == null ? "" : s)
1919
.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
2020
.replace(/"/g, "&quot;").replace(/'/g, "&#39;");
2121
}
22-
function isName(s) { return /^[A-Za-z_][A-Za-z0-9_]*$/.test(s); }
22+
// 标识符:支持英文与中文(中日韩统一表意文字)开头的变量名
23+
const ID_START = "A-Za-z_\\u4e00-\\u9fff";
24+
const ID_CHAR = "A-Za-z0-9_\\u4e00-\\u9fff";
25+
const NAME_RE = new RegExp("^[" + ID_START + "][" + ID_CHAR + "]*$");
26+
function isName(s) { return NAME_RE.test(String(s || "")); }
2327
const PY_TYPES = ["int", "float", "str", "bool", "list", "dict", "tuple"];
2428

2529
/* ---------------- 词法:表达式求值(安全,无 eval) ---------------- */
@@ -30,7 +34,7 @@
3034
if (c === " " || c === "\t") { i++; continue; }
3135
if (c === "(") { out.push({ t: "(", v: c }); i++; continue; }
3236
if (c === ")") { out.push({ t: ")", v: c }); i++; continue; }
33-
if (c === "+") { out.push({ t: "+", v: c }); i++; continue; }
37+
if ("+-*/%".indexOf(c) >= 0) { out.push({ t: c, v: c }); i++; continue; }
3438
const two = expr.substr(i, 2);
3539
if (two === "==" || two === "!=" || two === "<=" || two === ">=") { out.push({ t: two, v: two }); i += 2; continue; }
3640
if (c === "<" || c === ">") { out.push({ t: c, v: c }); i++; continue; }
@@ -39,14 +43,14 @@
3943
while (j < n && expr[j] !== q) { s += expr[j]; j++; }
4044
out.push({ t: "str", v: s }); i = Math.min(j + 1, n); continue;
4145
}
42-
if (/[0-9]/.test(c) || (c === "-" && /[0-9]/.test(expr[i + 1]))) {
46+
if (/[0-9]/.test(c)) {
4347
let j = i; let num = "";
44-
while (j < n && /[0-9._]/.test(expr[j])) { num += expr[j]; j++; }
45-
out.push({ t: "num", v: Number(num.replace(/[^0-9.]/g, "")) }); i = j; continue;
48+
while (j < n && /[0-9.]/.test(expr[j])) { num += expr[j]; j++; }
49+
out.push({ t: "num", v: Number(num) }); i = j; continue;
4650
}
47-
if (/[A-Za-z_0-9]/.test(c)) {
51+
if (/[A-Za-z_\u4e00-\u9fff]/.test(c)) {
4852
let j = i; let w = "";
49-
while (j < n && /[A-Za-z0-9_]/.test(expr[j])) { w += expr[j]; j++; }
53+
while (j < n && /[A-Za-z0-9_\u4e00-\u9fff]/.test(expr[j])) { w += expr[j]; j++; }
5054
out.push({ t: (["and", "or", "not", "in"].indexOf(w) >= 0 ? w : "id"), v: w });
5155
i = j; continue;
5256
}
@@ -95,27 +99,42 @@
9599
if (w === "false") return false;
96100
if (w === "None") return "";
97101
if (Object.prototype.hasOwnProperty.call(vars, w)) return vars[w].value;
98-
return ""; // 未知裸标识符当空串处理(v0.01 约定)
102+
return ""; // 未知裸标识符当空串处理
99103
}
100104
return "";
101105
}
102-
function factor() { return primary(); }
103-
function term() {
104-
let v = factor();
105-
while (peek() && peek().t === "+") { next(); v = Number(v) + Number(factor()); }
106+
function unary() {
107+
if (peek() && peek().t === "-") { next(); return -Number(unary()); }
108+
return primary();
109+
}
110+
function mulDiv() {
111+
let v = unary();
112+
while (peek() && (peek().t === "*" || peek().t === "/" || peek().t === "%")) {
113+
const op = next().t; const r = unary();
114+
const a = Number(v), b = Number(r);
115+
v = op === "*" ? a * b : (op === "/" ? a / b : a % b);
116+
}
117+
return v;
118+
}
119+
function addSub() {
120+
let v = mulDiv();
121+
while (peek() && (peek().t === "+" || peek().t === "-")) {
122+
const op = next().t; const r = mulDiv();
123+
v = Number(v) + (op === "+" ? Number(r) : -Number(r));
124+
}
106125
return v;
107126
}
108127
function cmp() {
109-
let l = term();
128+
let l = addSub();
110129
while (peek() && ["==", "!=", "<", ">", "<=", ">=", "in"].indexOf(peek().t) >= 0) {
111-
const op = next().t; const r = term(); l = applyCmp(op, l, r);
130+
const op = next().t; const r = addSub(); l = applyCmp(op, l, r);
112131
}
113132
return l;
114133
}
115134
function notExpr() { if (peek() && peek().t === "not") { next(); return !truthy(notExpr()); } return cmp(); }
116135
function andExpr() { let v = notExpr(); while (peek() && peek().t === "and") { next(); v = truthy(v) && truthy(notExpr()); } return v; }
117136
function orExpr() { let v = andExpr(); while (peek() && peek().t === "or") { next(); v = truthy(v) || truthy(andExpr()); } return v; }
118-
try { return truthy(orExpr()); } catch (e) { return false; }
137+
try { return orExpr(); } catch (e) { return false; }
119138
}
120139

121140
/* ---------------- 判断头解析 ---------------- */
@@ -136,8 +155,8 @@
136155
function classify(line) {
137156
// 返回 {kind, ...}
138157
if (!line || line.trim() === "" ) return { kind: "blank" };
139-
// it 变量声明
140-
const itm = line.match(/^it\s+([A-Za-z_][A-Za-z0-9_]*)(?:\s+([A-Za-z0-9_]+))?(?:\s+(.*))?$/);
158+
// it 变量声明(支持中文变量名)
159+
const itm = line.match(/^it\s+([A-Za-z_\u4e00-\u9fff][A-Za-z0-9_\u4e00-\u9fff]*)(?:\s+([A-Za-z0-9_]+))?(?:\s+(.*))?$/);
141160
if (itm) {
142161
const name = itm[1];
143162
let type = itm[2] || "";
@@ -150,11 +169,12 @@
150169
if (type !== "t" && type !== "p" && type) type = "ordinary"; // int/str... 归普通
151170
return { kind: "it", name, vtype: kind, type, value };
152171
}
153-
// in 展示:name in t / name in p (坐标在本版本不启用,默认居中)
154-
const inm = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s+in\s+(t|p|text|img)\b/i);
172+
// in 展示:name in t 标题 / in s 副标题 / in b 正文 / in p 图片 / in link 链接
173+
const inm = line.match(/^([A-Za-z_\u4e00-\u9fff][A-Za-z0-9_\u4e00-\u9fff]*)\s+in\s+(t|p|s|b|text|img|sub|body|link)\b/i);
155174
if (inm) {
156175
const mode = inm[2].toLowerCase();
157-
return { kind: "in", name: inm[1], mode: (mode === "p" || mode === "img") ? "img" : "text" };
176+
const map = { t: "text", text: "text", p: "img", img: "img", s: "sub", sub: "sub", b: "body", body: "body", link: "link" };
177+
return { kind: "in", name: inm[1], mode: map[mode] || "text" };
158178
}
159179
const cond = parseCondHeader(line);
160180
if (cond) return { kind: "cond", ...cond };
@@ -255,7 +275,12 @@
255275
nodes.forEach(function (n) {
256276
if (n.kind === "it") {
257277
if (!vars[n.name]) {
258-
vars[n.name] = { type: n.vtype || "ordinary", value: n.value || "", isImg: (n.vtype === "img") };
278+
let val = n.value || "";
279+
// 数值变量:普通变量的值为纯数字时存为 number,便于算术与数值比较
280+
if (n.vtype !== "text" && n.vtype !== "img" && val !== "" && !isNaN(Number(val))) {
281+
val = Number(val);
282+
}
283+
vars[n.name] = { type: n.vtype || "ordinary", value: val, isImg: (n.vtype === "img") };
259284
}
260285
} else if (n.kind === "cond") {
261286
collectVars(n.body, vars);
@@ -321,9 +346,13 @@
321346
" background:transparent;color:#f2e9da;cursor:pointer;text-decoration:none;font-size:13px;}",
322347
".hic-bar button:hover,.hic-bar a:hover{border-color:#d9ae6b;color:#d9ae6b;}",
323348
".hic-bar .on{border-color:#d9ae6b;color:#15100b;background:#d9ae6b;font-weight:600;}",
324-
".hic-item{max-width:860px;margin:40px auto;padding:0 20px;text-align:center;}",
349+
".hic-item{max-width:860px;margin:34px auto;padding:0 20px;text-align:center;}",
325350
".hic-text{font-size:42px;font-weight:800;letter-spacing:.5px;line-height:1.3;}",
326-
".hic-sub{font-size:20px;color:#cbb995;margin-top:8px;}",
351+
".hic-sub{font-size:24px;font-weight:600;color:#cbb995;margin-top:10px;}",
352+
".hic-body{max-width:640px;text-align:left;font-size:16px;color:#cfc3ab;line-height:1.9;}",
353+
".hic-linkwrap{text-align:center;}",
354+
".hic-href{display:inline-block;margin-top:8px;padding:12px 26px;border-radius:999px;border:1px solid rgba(217,174,107,.5);color:#d9ae6b;font-size:15px;text-decoration:none;transition:background .2s,color .2s;}",
355+
".hic-href:hover{background:#d9ae6b;color:#15100b;}",
327356
".hic-link{margin-top:14px;font-size:14px;color:#8d7f63;}",
328357
".hic-imgwrap{max-width:860px;margin:40px auto;padding:0 20px;text-align:center;}",
329358
".hic-img{max-width:100%;max-height:70vh;border-radius:16px;box-shadow:0 18px 50px rgba(0,0,0,.4);}",
@@ -340,6 +369,17 @@
340369
if (it.kind === "display" && it.mode === "text") {
341370
return '<p class="hic-item hic-text">' + esc(it.value) + "</p>";
342371
}
372+
if (it.kind === "display" && it.mode === "sub") {
373+
return '<p class="hic-item hic-sub">' + esc(it.value) + "</p>";
374+
}
375+
if (it.kind === "display" && it.mode === "body") {
376+
return '<p class="hic-item hic-body">' + esc(it.value) + "</p>";
377+
}
378+
if (it.kind === "display" && it.mode === "link") {
379+
const href = it.value ? String(it.value) : "#";
380+
return '<div class="hic-item hic-linkwrap"><a class="hic-href" href="' + esc(href) +
381+
'" target="_blank" rel="noopener">' + esc(it.value) + "</a></div>";
382+
}
343383
if (it.kind === "display" && it.mode === "img") {
344384
const src = it.value ? esc(it.value) : "";
345385
return '<div class="hic-item hic-imgwrap"><img class="hic-img' + (src ? "" : " hic-void") + '" src="' + src +
@@ -357,7 +397,7 @@
357397
(href.sameDoc ? ' data-hc-goto="' + esc(href.pageKey) + '"' : "") + ">前往 " + esc(href.label || label) + " 页面 →</a></div>";
358398
}
359399
if (it.kind === "textline") {
360-
return '<p class="hic-item hic-sub">' + esc(it.text) + "</p>";
400+
return '<p class="hic-item hic-body">' + esc(it.text) + "</p>";
361401
}
362402
return "";
363403
}).join("\n");

ide/js/store.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* ============================================================
2-
* HC v0.01 —— 数据层 store.js
2+
* HC v0.02 —— 数据层 store.js
33
* 职责:
44
* 1) 项目 / 页面 CRUD,localStorage 本地持久化
55
* 2) 首次零项目引导判定(hasAnyProject / lastActive)
@@ -21,7 +21,7 @@
2121
"use strict";
2222

2323
const KEY = "HICODE_DATA";
24-
const STORE_VERSION = "v0.01";
24+
const STORE_VERSION = "v0.02";
2525

2626
function uuid() {
2727
return "h" + Date.now().toString(36) + Math.random().toString(36).slice(2, 8);

ide/test/hic.test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,54 @@ ok(z instanceof Uint8Array && z.length > 30, "zip字节生成");
8585
// 校验 PK 头
8686
ok(z[0] === 0x50 && z[1] === 0x4b, "zip魔数PK");
8787

88+
// ---- 9. v0.02 中文变量名 ----
89+
r = HC.classify("it 标题 t 你好世界");
90+
ok(r.kind === "it" && r.name === "标题" && r.vtype === "text" && r.value === "你好世界", "中文变量名 it");
91+
r = HC.classify("标题 in t");
92+
ok(r.kind === "in" && r.name === "标题" && r.mode === "text", "中文变量名 in t");
93+
r = HC.classify("it 用户年龄 int 18");
94+
ok(r.kind === "it" && r.name === "用户年龄" && r.vtype === "ordinary", "中文变量名 it int");
95+
96+
// ---- 10. v0.02 四则运算 / 优先级 / 一元负号 ----
97+
ok(HC.evalExpr("2 + 3 * 4", {}) === 14, "乘法优先级高于加法");
98+
ok(HC.evalExpr("(2 + 3) * 4", {}) === 20, "括号改变优先级");
99+
ok(HC.evalExpr("10 % 3", {}) === 1, "取模 %");
100+
ok(HC.evalExpr("2.5 * 2", {}) === 5, "浮点乘法");
101+
ok(HC.evalExpr("-5 + 3", {}) === -2, "一元负号");
102+
ok(HC.evalExpr("10 - 2 - 3", {}) === 5, "连续减法");
103+
ok(HC.evalExpr("x + 2 == 20", { x: { value: 18 } }) === true, "变量参与算术");
104+
ok(HC.evalExpr("x > 10", { x: { value: 3 } }) === false, "数值比较(多位)");
105+
ok(HC.evalExpr("x > 10", { x: { value: 30 } }) === true, "数值比较(多位)2");
106+
107+
// ---- 11. v0.02 展示样式分级 + 链接 + 数值变量 ----
108+
const p2 = {
109+
name: "demo",
110+
code: [
111+
"it 标题 t 你好世界",
112+
"it 副标题 t 这是副标题",
113+
"it 正文 t 这是一段正文内容",
114+
"it 链接 t https://example.com",
115+
"it 数量 int 18",
116+
"标题 in t",
117+
"副标题 in s",
118+
"正文 in b",
119+
"链接 in link",
120+
"if 数量 + 2 == 20:",
121+
" 标题 in t"
122+
].join("\n"),
123+
images: {}
124+
};
125+
const r2 = HC.processPage(p2, {});
126+
ok(Object.keys(r2.vars).length === 5, "中文变量收集到5个");
127+
ok(r2.vars["标题"].value === "你好世界", "中文变量名取值");
128+
ok(typeof r2.vars["数量"].value === "number" && r2.vars["数量"].value === 18, "数值变量存为 number");
129+
const displayModes = r2.items.filter(x => x.kind === "display").map(x => x.mode);
130+
ok(displayModes.indexOf("sub") >= 0, "sub 副标题模式");
131+
ok(displayModes.indexOf("body") >= 0, "body 正文模式");
132+
ok(displayModes.indexOf("link") >= 0, "link 链接模式");
133+
const built = HC.buildSinglePageHtml({ name: "demo" }, p2);
134+
ok(built.indexOf('class="hic-href"') >= 0, "链接渲染含 hic-href");
135+
ok(built.indexOf("hic-sub") >= 0, "副标题渲染含 hic-sub");
136+
88137
console.log("\n通过 " + pass + " 项,失败 " + fail + " 项");
89138
process.exit(fail ? 1 : 0);

ide/test/runner.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<html lang="zh-CN">
33
<head><meta charset="UTF-8"><title>HC 引擎浏览器测试</title></head>
44
<body style="font-family:monospace;background:#14110d;color:#e8ddc8;padding:20px;line-height:1.7">
5-
<h3 style="color:#d9ae6b">HC v0.01 引擎浏览器测试</h3>
5+
<h3 style="color:#d9ae6b">HC v0.02 引擎浏览器测试</h3>
66
<pre id="out">运行中...</pre>
77
<script src="../js/hic.js"></script>
88
<script>

0 commit comments

Comments
 (0)