commit 973db5f00aaee3d205670265c55feb4cb0b8b9c6 Author: Mike Nolan Date: Sun Jun 21 21:05:24 2026 -0500 first commit diff --git a/Tesses.CrossLisp.CompileTool/.gitignore b/Tesses.CrossLisp.CompileTool/.gitignore new file mode 100644 index 0000000..4b90757 --- /dev/null +++ b/Tesses.CrossLisp.CompileTool/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +bin +obj +publish +thumbs.db diff --git a/Tesses.CrossLisp.CompileTool/cross.json b/Tesses.CrossLisp.CompileTool/cross.json new file mode 100644 index 0000000..c5e8320 --- /dev/null +++ b/Tesses.CrossLisp.CompileTool/cross.json @@ -0,0 +1,11 @@ +{ + "$schema": "https:\/\/crosslang.tesseslanguage.com\/\/schema\/cross-json-schema.json", + "info": { + "type": "compile_tool" + }, + "name": "Tesses.CrossLisp.CompileTool", + "project_dependencies": [ + "..\/Tesses.CrossLisp.Runtime" + ], + "version": "1.0.0.0-prod" +} \ No newline at end of file diff --git a/Tesses.CrossLisp.CompileTool/src/main.tcross b/Tesses.CrossLisp.CompileTool/src/main.tcross new file mode 100644 index 0000000..19ed652 --- /dev/null +++ b/Tesses.CrossLisp.CompileTool/src/main.tcross @@ -0,0 +1,39 @@ +func RunTool(cfg) +{ + //cfg.Project is the project Subdirfilesystem + //cfg.ProjectInfo is the info property parsed from the cross.json file in project root + const crossConf = "/cross.json"; + + if(!cfg.Project.FileExists(crossConf)) return; + var srcDir = "src"; + var configData = Json.Decode(FS.ReadAllText(FS.Local,crossConf)); + if(TypeOf(configData.source_directory) != "Undefined") + srcDir = configData.source_directory; + + + + func walk_for_source(sourceDir) + { + if(cfg.Project.DirectoryExists(sourceDir)) + each(var file : cfg.Project.EnumeratePaths(sourceDir)) + { + if(cfg.Project.RegularFileExists(file) && file.GetExtension()==".lisp") + { + var src = { + FileName = file.ToString(), + Source = CrossLisp.toCrossLangText(CrossLisp.parseLisp(FS.ReadAllText(cfg.Project, file))) + }; + cfg.GeneratedSource.Add(src); + } + else if(cfg.Project.DirectoryExists(file)) + { + walk_for_source(file); + } + } + } + + walk_for_source(srcDir); + + + //this runs with full access to vm so be careful when allowing these +} diff --git a/Tesses.CrossLisp.Example/.gitignore b/Tesses.CrossLisp.Example/.gitignore new file mode 100644 index 0000000..4b90757 --- /dev/null +++ b/Tesses.CrossLisp.Example/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +bin +obj +publish +thumbs.db diff --git a/Tesses.CrossLisp.Example/cross.json b/Tesses.CrossLisp.Example/cross.json new file mode 100644 index 0000000..e6500ac --- /dev/null +++ b/Tesses.CrossLisp.Example/cross.json @@ -0,0 +1,15 @@ +{ + "$schema": "https:\/\/crosslang.tesseslanguage.com\/\/schema\/cross-json-schema.json", + "info": { + "description": "Change Me", + "short_name": "changeme", + "short_name_pretty": "Change Me", + "type": "console" + }, + "name": "Tesses.CrossLisp.Example", + "project_dependencies": [ + "..\/Tesses.CrossLisp.CompileTool", + "..\/Tesses.CrossLisp.Runtime" + ], + "version": "1.0.0.0-prod" +} \ No newline at end of file diff --git a/Tesses.CrossLisp.Example/src/main.lisp b/Tesses.CrossLisp.Example/src/main.lisp new file mode 100644 index 0000000..c7fd7c7 --- /dev/null +++ b/Tesses.CrossLisp.Example/src/main.lisp @@ -0,0 +1,9 @@ +(defun print(text) + "Print To Console" + (call Console "WriteLine" text) +) +(defun main(args) + "My Main Function" + (print (* 6 7)) + 1 +) diff --git a/Tesses.CrossLisp.Runtime/.gitignore b/Tesses.CrossLisp.Runtime/.gitignore new file mode 100644 index 0000000..4b90757 --- /dev/null +++ b/Tesses.CrossLisp.Runtime/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +bin +obj +publish +thumbs.db diff --git a/Tesses.CrossLisp.Runtime/cross.json b/Tesses.CrossLisp.Runtime/cross.json new file mode 100644 index 0000000..db9bed9 --- /dev/null +++ b/Tesses.CrossLisp.Runtime/cross.json @@ -0,0 +1,8 @@ +{ + "$schema": "https:\/\/crosslang.tesseslanguage.com\/\/schema\/cross-json-schema.json", + "info": { + "type": "lib" + }, + "name": "Tesses.CrossLisp.Runtime", + "version": "1.0.0.0-prod" +} \ No newline at end of file diff --git a/Tesses.CrossLisp.Runtime/src/mylib.tcross b/Tesses.CrossLisp.Runtime/src/mylib.tcross new file mode 100644 index 0000000..793ba5b --- /dev/null +++ b/Tesses.CrossLisp.Runtime/src/mylib.tcross @@ -0,0 +1,462 @@ + +func CrossLisp.isString(v) +{ + if(!TypeIsList(v)) return false; + if(v.Length < 2) return false; + return v[0] == "$string"; +} +func CrossLisp.getString(v) +{ + if(!CrossLisp.isString(v)) return ""; + return v[1]; +} + +func CrossLisp.asCrossLangList(val) +{ + if(TypeIsLong(val)) return val.ToString(); + if(TypeIsDouble(val)) return val.ToString(); + if(TypeIsString(val)) return val.Escape(true); + + if(TypeIsList(val)) + { + var str = "["; + + for(var i = 0; i < val.Length; i++) + { + if(i > 0) + str += ", "; + str += CrossLisp.asCrossLangList(val[i]); + } + + + return str + "]"; + } + + return "null"; +} + +func CrossLisp.toCrossLangExpression(val) +{ + + + if(val == "nil") return "[]"; + if(TypeIsLong(val)) return val.ToString(); + if(TypeIsDouble(val)) return val.ToString(); + if(TypeIsString(val)) return val; + + if(TypeIsList(val)) + { + if(val.Length == 0) return "[]"; + switch(val[0]) + { + case "+": + { + var fnCall = "("; + + if(val.Length == 1) + fnCall += "0"; + + for(var i = 1; i < val.Length; i++) + { + if(i > 1) + fnCall += " + "; + fnCall += CrossLisp.toCrossLangExpression(val[i]); + } + + return fnCall + ")"; + } + break; + case "-": + { + var fnCall = "("; + + if(val.Length == 1) + fnCall += "0"; + + for(var i = 1; i < val.Length; i++) + { + if(i > 1) + fnCall += " - "; + fnCall += CrossLisp.toCrossLangExpression(val[i]); + } + + return fnCall + ")"; + } + break; + case "*": + { + var fnCall = "("; + + if(val.Length == 1) + fnCall += "0"; + + for(var i = 1; i < val.Length; i++) + { + if(i > 1) + fnCall += " * "; + fnCall += CrossLisp.toCrossLangExpression(val[i]); + } + + return fnCall + ")"; + } + break; + case "/": + { + var fnCall = "("; + + if(val.Length == 1) + fnCall += "0"; + + for(var i = 1; i < val.Length; i++) + { + if(i > 1) + fnCall += " / "; + fnCall += CrossLisp.toCrossLangExpression(val[i]); + } + + return fnCall + ")"; + } + break; + case "%": + { + var fnCall = "("; + + if(val.Length == 1) + fnCall += "0"; + + for(var i = 1; i < val.Length; i++) + { + if(i > 1) + fnCall += " % "; + fnCall += CrossLisp.toCrossLangExpression(val[i]); + } + + return fnCall + ")"; + } + break; + case "quote": + { + if(val.Count > 1) + return CrossLisp.asCrossLangList(val[1]); + } + break; + case "call": + { + if(val.Length >= 3 && CrossLisp.isString(val[2])) + { + var fnCall = "(" + CrossLisp.toCrossLangExpression(val[1]) + $").{CrossLisp.getString(val[2])}("; + for(var i = 3; i < val.Length; i++) + { + if(i > 3) + fnCall += ", "; + fnCall += CrossLisp.toCrossLangExpression(val[i]); + } + return fnCall + ")"; + } + //(call Console "WriteLine" args...) + } + case "eval": + { + if(val.Length == 2) + { + return "VM.Eval(\"return \" + CrossLisp.toCrossLangExpression(" + CrossLisp.toCrossLangExpression(val[1]) + ") + \";\")"; + } + } + break; + case "setq": + { + //(setq val 42) + if(val.Length == 3 && TypeIsString(name)) + { + return "(" + name + "=" + CrossLisp.toCrossLangExpression(val[2]) + ")"; + } + } + break; + case "dict": + { + var dict = "{"; + for(var i = 1; i < val.Length; i++) + { + if(i > 1) + dict += ", "; + if(TypeIsList(val[i]) && val[i].Length == 2 && TypeIsString(val[i][0])) + { + dict += val[i][0] + " = " + CrossLisp.toCrossLangExpression(val[i][1]); + } + else { + throw "Must look like this (dict (key1 value) (key2 value))"; + } + } + return dict + "}"; + } + break; + case "$string": + { + if(val.Length == 2) + { + return val[1].Escape(true); + } + } + break; + default: + { + var fnCall = val[0] + "("; + + for(var i = 1; i < val.Length; i++) + { + if(i > 1) + fnCall += ", "; + fnCall += CrossLisp.toCrossLangExpression(val[i]); + } + + return fnCall + ")"; + } + break; + } + } + + return "[]"; +} + +func CrossLisp.toCrossLangText(list) +{ + + + + var code = ""; + each(var item : list) + { + if(TypeIsList(item) && item.Length > 0) + { + if(item[0] == "defun") + { + if(TypeIsString(item[1])) + { + var hasDoc = false; + if(CrossLisp.isString(item[3])) + { + hasDoc=true; + code += "\n/^ " + CrossLisp.getString(item[3]).Replace("^","^^") + " ^/\n"; + } + + code += $"func {item[1]}("; + if(TypeIsList(item[2])) + { + for(var i = 0; i < item[2].Length; i++) + { + if(i > 0) code += ", "; + code += item[2][i]; + } + } + code += "){"; + for(var i = hasDoc ? 4 : 3; i < item.Length; i++) + { + if(i + 1 == item.Length) + code += "return "; + code += CrossLisp.toCrossLangExpression(item[i]); + code += ";"; + } + + code += "}"; + } + } + } + } + + return code; +} +func CrossLisp.parseLisp(text) +{ + //(+ "Hi" 42) + //["+",["$string","Hi"], 42] + + //(+ 'hi 42) + //["+",["quote",["hi"]],42] + + var builder = ""; + + var tokens = []; + + func flush() + { + if(builder.Length > 0) { + if(builder[0] >= '0' && builder[0] <= '9') + { + if(builder.Contains(".")) + { + const dbl = ParseDouble(builder); + tokens.Add({ + type = "ident", + value = dbl + }); + } + else { + const lng = ParseLong(builder); + tokens.Add({ + type = "ident", + value = lng + }); + } + } + else { + + tokens.Add({ + type = "ident", + value = builder + }); + } + + builder = ""; + } + + } + + for(var i = 0; i < text.Length; i++) + { + switch(text[i]) + { + case '(': + { + flush(); + tokens.Add({ + type="(" + }); + } + break; + case ')': + { + flush(); + tokens.Add({ + type=")" + }); + } + break; + case '\'': + { + flush(); + tokens.Add({ + type="'" + }); + } + break; + + break; + case '\\': + builder += text[++i]; + break; + case '\"': + { + flush(); + i++; + var strtext = ""; + for(; (i < text.Length) && text[i] != '\"'; i++) + { + if(text[i] == '\\') + { + i++; + if(i < text.Length) + { + if(text[i] == 'n') + { + strtext += "\n"; + } + else if(text[i] == 'r') + { + strtext += "\r"; + } + else if(text[i] == 't') + { + strtext += "\t"; + } + else if(text[i] == 'v') + { + strtext += "\v"; + } + else if(text[i] == 'e') + { + strtext += "\e"; + } + else if(text[i] == 'x') + { + if(i + 2 < text.Length) + { + i++; + strtext += Net.Http.UrlPathDecode($"%{text[i]}{text[i+1]}"); + i++; + + } + } + else { + strtext += text[i]; + } + } + } + else + { + strtext += text[i]; + } + } + tokens.Add({ + type = "$string", + value = strtext + }); + } + break; + case ' ': + case '\n': + case '\r': + case '\t': + flush(); + break; + default: + builder += text[i]; + break; + } + } + + flush(); + + + var off = 0; + + + func parse() + { + if(tokens[off].type == "'") + { + off++; + return ["quote",parse()]; + } + if(tokens[off].type == "(") + { + off++; + var ls = []; + while(tokens[off].type != ")") + { + ls.Add(parse()); + } + off++; + return ls; + } + + if(tokens[off].type == "ident") + { + return tokens[off++].value; + } + + if(tokens[off].type == "$string") + { + return ["$string",tokens[off++].value]; + } + + return []; + } + + const ls = []; + + while(off < tokens.Length) + ls.Add(parse()); + return ls; +} + +//Console.WriteLine(CrossLisp.toCrossLangText(parseLisp(""))); \ No newline at end of file