构建用于自动网页生成的迷你编程语言,核心是通过自定义简洁语法描述网页结构,再通过解析和转换逻辑生成最终HTML代码,整个过程可以分为语法设计、解析实现、代码生成三个核心阶段。

第一步:设计迷你编程语言的语法规则
首先需要明确语言要支持的功能,针对网页生成场景,我们可以设计如下基础语法:
- 用
page关键字定义页面根节点,支持设置页面标题 - 用
div、p、h1等关键字对应HTML标签,支持添加class、id等属性 - 用缩进表示标签的嵌套关系,不需要额外的闭合符号
- 支持变量定义和引用,方便复用重复内容
示例语法代码如下:
page title=我的第一个页面
h1 class=main-title 欢迎来到我的网站
div id=content
p 这是自动生成的第一段内容
p 这是自动生成的第二段内容
div class=footer
p 版权所有
第二步:实现词法和语法解析
解析过程分为词法分析和语法分析两步,词法分析负责将输入的语法文本拆分为一个个有意义的词法单元,语法分析则根据语法规则将词法单元组织成抽象语法树(AST)。
词法分析实现
我们可以用JavaScript实现简单的词法分析器,示例代码如下:
// 词法单元类型定义
const TokenType = {
PAGE: 'PAGE',
TAG: 'TAG',
ATTR: 'ATTR',
TEXT: 'TEXT',
INDENT: 'INDENT',
NEWLINE: 'NEWLINE',
EOF: 'EOF'
};
// 词法分析函数
function tokenize(input) {
const tokens = [];
let current = 0;
while (current < input.length) {
let char = input[current];
// 处理换行
if (char === 'n') {
tokens.push({ type: TokenType.NEWLINE, value: 'n' });
current++;
continue;
}
// 处理缩进
if (char === ' ' || char === 't') {
let indent = '';
while (current < input.length && (input[current] === ' ' || input[current] === 't')) {
indent += input[current];
current++;
}
tokens.push({ type: TokenType.INDENT, value: indent });
continue;
}
// 处理关键字和标签
if (/[a-zA-Z_]/.test(char)) {
let value = '';
while (current < input.length && /[a-zA-Z0-9_-]/.test(input[current])) {
value += input[current];
current++;
}
// 判断是否为page关键字
if (value === 'page') {
tokens.push({ type: TokenType.PAGE, value });
} else {
tokens.push({ type: TokenType.TAG, value });
}
continue;
}
// 处理属性赋值
if (char === '=') {
tokens.push({ type: TokenType.ATTR, value: '=' });
current++;
continue;
}
// 处理文本内容
if (char === '"' || char === "'") {
const quote = char;
let value = '';
current++; // 跳过开头的引号
while (current < input.length && input[current] !== quote) {
value += input[current];
current++;
}
current++; // 跳过结尾的引号
tokens.push({ type: TokenType.TEXT, value });
continue;
}
// 跳过其他无关字符
current++;
}
tokens.push({ type: TokenType.EOF, value: null });
return tokens;
}
语法分析生成AST
拿到词法单元后,我们可以编写语法分析器生成抽象语法树,示例代码如下:
// 抽象语法树节点类型
const ASTNodeType = {
PAGE: 'PAGE',
ELEMENT: 'ELEMENT',
TEXT: 'TEXT'
};
// 语法分析函数
function parse(tokens) {
let current = 0;
function walk() {
let token = tokens[current];
// 处理page节点
if (token.type === TokenType.PAGE) {
current++;
const node = {
type: ASTNodeType.PAGE,
title: '',
children: []
};
// 获取页面标题
if (tokens[current].type === TokenType.ATTR && tokens[current].value === '=') {
current++;
if (tokens[current].type === TokenType.TEXT) {
node.title = tokens[current].value;
current++;
}
}
// 处理换行和子节点
if (tokens[current].type === TokenType.NEWLINE) {
current++;
// 处理缩进后的子节点
while (current < tokens.length && tokens[current].type === TokenType.INDENT) {
current++; // 跳过缩进
node.children.push(walk());
if (tokens[current].type === TokenType.NEWLINE) {
current++;
}
}
}
return node;
}
// 处理普通元素节点
if (token.type === TokenType.TAG) {
const tagName = token.value;
current++;
const node = {
type: ASTNodeType.ELEMENT,
tag: tagName,
attrs: {},
children: [],
text: ''
};
// 处理属性
while (current < tokens.length && tokens[current].type === TokenType.ATTR) {
current++; // 跳过=
if (tokens[current].type === TokenType.TEXT) {
const attrName = Object.keys(node.attrs).length === 0 ? 'class' : 'id';
node.attrs[attrName] = tokens[current].value;
current++;
}
}
// 处理文本内容
if (tokens[current].type === TokenType.TEXT) {
node.text = tokens[current].value;
current++;
}
// 处理子节点
if (tokens[current].type === TokenType.NEWLINE) {
current++;
while (current < tokens.length && tokens[current].type === TokenType.INDENT) {
current++; // 跳过缩进
node.children.push(walk());
if (tokens[current].type === TokenType.NEWLINE) {
current++;
}
}
}
return node;
}
}
const ast = walk();
return ast;
}
第三步:将AST转换为HTML代码
得到抽象语法树后,我们只需要遍历AST,按照节点类型生成对应的HTML代码即可,示例代码如下:
// 生成属性字符串
function generateAttrs(attrs) {
return Object.entries(attrs)
.map(([key, value]) => ` ${key}="${value}"`)
.join('');
}
// 将AST转换为HTML
function generateHTML(ast) {
if (ast.type === ASTNodeType.PAGE) {
let html = `<!DOCTYPE html>n<html>n<head>n<title>${ast.title}</title>n</head>n<body>n`;
ast.children.forEach(child => {
html += generateHTML(child);
});
html += '</body>n</html>';
return html;
}
if (ast.type === ASTNodeType.ELEMENT) {
let html = `<${ast.tag}${generateAttrs(ast.attrs)}>`;
if (ast.text) {
html += ast.text;
}
ast.children.forEach(child => {
html += generateHTML(child);
});
html += `</${ast.tag}>n`;
return html;
}
return '';
}
// 完整流程调用示例
const input = `page title=我的第一个页面
h1 class=main-title 欢迎来到我的网站
div id=content
p 这是自动生成的第一段内容
p 这是自动生成的第二段内容
div class=footer
p 版权所有`;
const tokens = tokenize(input);
const ast = parse(tokens);
const html = generateHTML(ast);
console.log(html);
功能扩展建议
完成基础功能后,还可以为迷你编程语言添加更多实用特性:
- 支持条件判断和循环语法,方便生成动态内容
- 添加内置函数,支持引入外部文件、处理字符串等能力
- 增加错误提示功能,当输入语法不符合规则时给出明确的报错信息
- 支持生成CSS和JavaScript代码,实现更完整的网页生成能力
整个构建过程不需要深厚的编译原理知识,核心是先明确需求设计语法,再逐步实现解析和转换逻辑,最终就能得到适配自身需求的自动网页生成工具。