First commit

This commit is contained in:
2024-12-30 05:56:46 -06:00
commit 5c8d97d8d4
20 changed files with 1734 additions and 0 deletions

View File

@@ -0,0 +1,818 @@
func crossmarkuplexer(data)
{
var tokens = [];
var i = 0;
var peeked=null;
var inSpecial=false;
var builder = "";
func read()
{
if(peeked)
{
var myPeeked=peeked;
peeked = null;
return myPeeked;
}
if(i < data.Length) {
var rc = data[i++];
return rc;
}
return null;
}
func peek()
{
if(peeked) return peeked;
peeked = read();
return peeked;
}
func flush()
{
if(builder.Count > 0)
{
tokens.Add({
Type = inSpecial ? "Identifier" : "Text",
Text = builder
});
builder = "";
}
}
func ReadChar()
{
var r2 = read();
if(!r2) return null;
if(r2 == '\\')
{
r2 = read();
if(!r2)
{
return null;
}
return $"\\{r2}";
}
return r2.ToString();
}
func ReadString()
{
var str = "";
while(var myChar = ReadChar())
{
if(myChar == "\"") break;
str += myChar;
}
return str;
}
while(r = read())
{
var p = peek();
if(!inSpecial && r == '<')
{
if(p == '?')
{
read();
flush();
tokens.Add({
Type = "EnterSpecial"
});
inSpecial=true;
}
else
{
builder += "<";
}
}
else if(inSpecial && (r == '\r' || r == '\n' || r == ' ' || r == '\t'))
{
flush();
}
else if(inSpecial && r == '\"')
{
flush();
var myStr = ReadString();
tokens.Add({
Type = "String",
Text = myStr
});
}
else if(inSpecial && r == '\'')
{
flush();
var myChr = ReadChar();
tokens.Add({
Type = "Char",
Text = myChr
});
read();
}
else if(inSpecial && r == '=')
{
flush();
tokens.Add({
Type = "Equals"
});
}
else if(inSpecial && r == '(')
{
flush();
tokens.Add({
Type = "OpenParen"
});
}
else if(inSpecial && r == ')')
{
flush();
tokens.Add({
Type = "CloseParen"
});
}
else if(inSpecial && r == ',')
{
flush();
tokens.Add({
Type = "Comma"
});
}
else if(inSpecial && r == '?')
{
if(p == '?')
{
read();
builder += "?";
}
else if(p == '>')
{
read();
flush();
tokens.Add({
Type = "ExitSpecial"
});
inSpecial=false;
}
else
{
builder += read().ToString();
}
}
else
{
builder += r.ToString();
}
}
flush();
return tokens;
}
func crossmarkupparser(tokens)
{
var i = 0;
func NotEnd()
{
if(i + 3 > tokens.Count) return false;
if(tokens[i].Type != "EnterSpecial")
return true;
if(tokens[i+1].Type != "Identifier")
return true;
if(tokens[i+1].Text != "end")
return true;
if(tokens[i+2].Type != "ExitSpecial")
return true;
return false;
}
func EnsureEnd()
{
if(NotEnd())
{
throw "Must end with <?end?>";
}
i += 3;
}
func TokenTypeIs(type)
{
return i < tokens.Length && tokens[i].Type == type;
}
func EnsureTokenType(type)
{
if(!TokenTypeIs(type))
{
throw $"The token is {type} which is not {tokens[i].Type}";
}
i++;
}
func ConsumeIfTokenTypeIs(type)
{
if(TokenTypeIs(type)) i++;
}
func parse_node()
{
if(i < tokens.Length)
{
var myToken = tokens[i++];
if(myToken.Type == "EnterSpecial")
{
if(i < tokens.Length)
{
var myToken2 = tokens[i++];
if(myToken2.Type == "Identifier")
{
if(myToken2.Text == "page")
{
var pageargs = [];
var nodes =[];
var route = "/";
var router_function = "Router";
//its a page
EnsureTokenType("OpenParen");
while(!TokenTypeIs("CloseParen"))
{
ConsumeIfTokenTypeIs("Comma");
if(i < tokens.Length && tokens[i].Type == "Identifier")
{
pageargs.Add(tokens[i].Text);
i++;
}
}
EnsureTokenType("CloseParen");
while(!TokenTypeIs("ExitSpecial"))
{
if(TokenTypeIs("Identifier"))
{
var key = tokens[i++].Text;
EnsureTokenType("Equals");
if(TokenTypeIs("String"))
{
var value = tokens[i++].Text;
if(key == "route")
route = value;
else if(key == "router_function")
router_function = value;
else
throw $"Unknown property {key}";
}
else
{
throw "Not a string";
}
}
else
{
throw "Not an identifier";
}
}
EnsureTokenType("ExitSpecial");
while(NotEnd())
{
nodes.Add(parse_node());
}
EnsureEnd();
return {
Type = "PageNode",
Arguments = pageargs,
Route = route,
RouterFunction = router_function,
Nodes = nodes
};
}
else if(myToken2.Text == "component")
{
var pageargs = [];
var nodes =[];
var name = "Component";
//its a component
EnsureTokenType("OpenParen");
while(!TokenTypeIs("CloseParen"))
{
ConsumeIfTokenTypeIs("Comma");
if(i < tokens.Length && tokens[i].Type == "Identifier")
{
pageargs.Add(tokens[i].Text);
i++;
}
}
EnsureTokenType("CloseParen");
while(!TokenTypeIs("ExitSpecial"))
{
if(TokenTypeIs("Identifier"))
{
var key = tokens[i++].Text;
EnsureTokenType("Equals");
if(TokenTypeIs("String"))
{
var value = tokens[i++].Text;
if(key == "name")
name = value;
else
throw $"Unknown property {key}";
}
else
{
throw "Not a string";
}
}
else
{
throw "Not an identifier";
}
}
EnsureTokenType("ExitSpecial");
while(NotEnd())
{
nodes.Add(parse_node());
}
EnsureEnd();
return {
Type = "ComponentNode",
Arguments = pageargs,
Name = name,
Nodes = nodes
};
}
else if(myToken2.Text == "code")
{
var nodes = [];
while(!TokenTypeIs("ExitSpecial"))
{
if(i < tokens.Length)
nodes.Add(parse_node());
}
EnsureTokenType("ExitSpecial");
return {
Type = "CodeNode",
Nodes = nodes
};
}
else if(myToken2.Text == "resource")
{
var name = "";
var route = "";
var router_function = "Router";
while(!TokenTypeIs("ExitSpecial"))
{
if(TokenTypeIs("Identifier"))
{
var key = tokens[i++].Text;
EnsureTokenType("Equals");
if(TokenTypeIs("String"))
{
var value = tokens[i++].Text;
if(key == "name")
name = value;
else if(key == "route")
route = value;
else if(key == "router_function")
router_function = value;
else
throw $"Unknown property {key}";
}
else
{
throw "Not a string";
}
}
else
{
throw "Not an identifier";
}
}
EnsureTokenType("ExitSpecial");
return {
Type = "EmbedNode",
Name = name,
Route = route,
RouterFunction = router_function
};
}
else if(myToken2.Text == "expr")
{
var nodes = [];
while(!TokenTypeIs("ExitSpecial"))
{
if(i < tokens.Length)
nodes.Add(parse_node());
}
EnsureTokenType("ExitSpecial");
return {
Type = "ExprNode",
Nodes = nodes
};
}
else
{
EnsureTokenType("ExitSpecial");
var args = [];
while(NotEnd())
{
if(TokenTypeIs("Text"))
{
EnsureTokenType("Text");
}
else if(TokenTypeIs("EnterSpecial"))
{
EnsureTokenType("EnterSpecial");
var myToken3 = tokens[i++];
if(myToken3.Type == "Identifier")
{
if(myToken3.Text == "arg")
{
var nodes = [];
while(!TokenTypeIs("ExitSpecial"))
{
if(i < tokens.Length)
nodes.Add(parse_node());
}
EnsureTokenType("ExitSpecial");
args.Add({
Type = "ArgNode",
Nodes = nodes
});
}
else if(myToken3.Text == "arg_component")
{
var pageargs = [];
var nodes =[];
//its a component
EnsureTokenType("OpenParen");
while(!TokenTypeIs("CloseParen"))
{
ConsumeIfTokenTypeIs("Comma");
if(i < tokens.Length && tokens[i].Type == "Identifier")
{
pageargs.Add(tokens[i].Text);
i++;
}
}
EnsureTokenType("CloseParen");
EnsureTokenType("ExitSpecial");
while(NotEnd())
{
nodes.Add(parse_node());
}
EnsureEnd();
args.Add({
Type = "ArgComponentNode",
Arguments = pageargs,
Nodes = nodes
});
}
}
else
{
throw "Must be an identifier";
}
}
}
EnsureEnd();
return {
Type = "UseComponentNode",
Name = myToken2.Text,
Arguments = args
};
}
}
}
}
else if(myToken.Type == "Identifier")
{
return {
Type = "IdentifierNode",
Text = myToken.Text
};
}
else if(myToken.Type == "Text")
{
return {
Type = "TextNode",
Text = myToken.Text
};
}
else if(myToken.Type == "Char")
{
return {
Type = "CharNode",
Text = $"\'{myToken.Text}\'"
};
}
else if(myToken.Type == "String")
{
return {
Type = "StringNode",
Text = $"\"{myToken.Text}\""
};
}
else if(myToken.Type == "Equals" || myToken.Type == "OpenParen" || myToken.Type == "CloseParen" || myToken.Type == "Comma" || myToken.Type == "")
{
return {
Type = $"{myToken.Type}Node"
};
}
}
return null;
}
var nodes = [];
while(i < tokens.Length)
{
nodes.Add(parse_node());
}
return {
Type = "RootNode",
Nodes = nodes
};
}
func crossmarkupgen(ast)
{
var page_functions = [];
var code = "";
func add_to_page(name, code)
{
each(var item : page_functions)
{
if(item.Key == name)
{
item.Value += code;
return null;
}
}
page_functions.Add({
Key = name,
Value = code
});
}
func generate_node(node)
{
func myescape(txt)
{
var out = "";
each(var c : txt)
{
var cI = c.ToLong();
if(cI < 32 || cI > 126)
{
out += $"\\x{(cI % 0xFF).ToHexString(2)}";
}
else if(c == '\"')
{
out += "\\\"";
}
else if(c == '\'')
{
out += "\\\'";
}
else if(c == '\\')
{
out += "\\\\";
}
else {
out += c;
}
}
return out;
}
if(node.Type == "PageNode")
{
var code = $"if(ctx.Path == \"{node.Route}\")";
code += "{__writer = ctx.OpenResponseStream(); ";
each(var a : node.Arguments)
{
code += $"var {a} = ctx.QueryParams.TryGetFirst(\"{a}\");";
}
each(var item : node.Nodes)
{
code += generate_node(item);
}
code += " __writer.Close(); return true;}";
add_to_page(node.RouterFunction,code);
return "";
}
else if(node.Type == "EmbedNode")
{
var code = $"if(ctx.Path == \"{node.Route}\")";
code += "{";
code += $"ctx.WithMimeType(Net.Http.MimeType(\"{node.Name}\")).SendBytes(embed(\"{node.Name}\"));";
code += "return true;";
code += "}";
}
else if(node.Type == "TextNode")
{
return $"write(\"{myescape(node.Text)}\");";
}
else if(node.Type == "ExprNode")
{
var code = "write(";
each(var item : node.Nodes)
{
code += generate_node(item);
}
code += ");";
return code;
}
else if(node.Type == "CodeNode")
{
var code = "";
each(var item : node.Nodes)
{
code += generate_node(item);
}
return code;
}
else if(node.Type == "ComponentNode")
{
var code = $"func {node.Name}(write,ctx";
each(var arg0 : node.Arguments)
{
code += $",{arg0}";
}
code += "){";
each(var n : node.Nodes)
{
code += generate_node(n);
}
code += "}";
return code;
}
else if(node.Type == "UseComponentNode")
{
var code = $"{node.Name}(write,ctx";
each(var arg : node.Arguments)
{
code += ",";
if(arg.Type == "ArgNode")
{
each(var itm : arg.Nodes)
{
code += generate_node(itm);
}
}
else if(arg.Type == "ArgComponentNode")
{
code += "(write,ctx";
each(var arg0 : arg.Arguments)
{
code += $",{arg0}";
}
code += ")=>{";
each(var n : arg.Nodes)
{
code += generate_node(n);
}
code += "}";
}
}
code += ");";
return code;
}
else if(node.Type == "CharNode")
{
return $" {node.Text} ";
}
else if(node.Type == "StringNode")
{
return $" {node.Text} ";
}
else if(node.Type == "IdentifierNode")
{
return $" {node.Text} ";
}
else if(node.Type == "OpenParenNode")
{
return " ( ";
}
else if(node.Type == "CloseParenNode")
{
return " ) ";
}
else if(node.Type == "CommaNode")
{
return " , ";
}
else if(node.Type == "EqualsNode")
{
return " = ";
}
else if(node.Type == "RootNode")
{
var code = "";
each(var item : node.Nodes)
{
if(item.Type != "TextNode")
code += generate_node(item);
}
each(var pg : page_functions)
{
code += $"func {pg.Key}(ctx)";
code += "{ ctx.WithMimeType(\"text/html\");";
code += "var __writer = null;";
code += "func write(__text){ __writer.WriteText(__text); }";
code += pg.Value;
code += "__writer.Close(); return false;}";
}
return code;
}
return "";
}
return generate_node(ast);
}
func EnumerateFiles(cfg,path)
{
var txt = "";
if(cfg.Project.DirectoryExists(path))
each(var file : cfg.Project.EnumeratePaths(path))
{
if(cfg.Project.RegularFileExists(file))
{
var f = cfg.Project.OpenFile(file, "rb");
var ms = FS.MemoryStream(true);
f.CopyTo(ms);
txt += ms.GetBytes().ToString();
f.Close();
ms.Close();
}
else if(cfg.Project.DirectoryExists(file))
{
EnumerateFiles(cfg, file);
}
}
return txt;
}
func RunTool(cfg)
{
var text = "";
text += EnumerateFiles(cfg, "/components");
text += EnumerateFiles(cfg, "/pages");
var c = crossmarkuplexer(text);
var r = crossmarkupparser(c);
var src = crossmarkupgen(r);
cfg.GeneratedSource.Add({Source = src, FileName="markup.tcross"});
}