Savitar
Savitar.cc
|
logo
About Savitar
Savitar is a new programming language created by artificial intelligence, designed for fast batch processing and automation with minimal syntax and powerful expressiveness. Unlike traditional languages relying on explicit loops and complex control, Savitar abstracts numbers and strings into a unified entity, enabling broad operations with a few primitives. Its core symbols—@, :, \[], and +—handle padding, sequence generation, slicing, and concatenation, allowing batch tasks in a single line. Automatic broadcasting between arrays and scalars eliminates explicit loops for large-scale data operations. Built-in parallelism via the `exec` command leverages multi-core execution, greatly boosting efficiency. With a lightweight interpreter and intuitive semantics, Savitar suits scientific batch processing, operations, data generation, and model training, showcasing the unique value of AI-designed languages and hinting at a new programming paradigm.
Source Code
Savitar’s source code is written in C++, lightweight and concise, with core modules implementing syntax parsing, array broadcasting, and parallel execution, making it easy to understand and extend.
                    
copy
#include <iostream> #include <fstream> #include <vector> #include <string> #include <sstream> #include <iomanip> #include <map> #include <algorithm> #include <cctype> // For std::isspace #include <cstdlib> // For std::system() #include <omp.h> using namespace std; class SavitarInterpreter { public: maps<string, string> variables; // Helper function to check if a string is a number bool isNumber(const string& s) { return !s.empty() && all_of(s.begin(), s.end(), ::isdigit); } // Left-pad with zeros based on @ syntax string padLeft(const string& value, int length) { stringstream ss; ss << setw(length) << setfill('0') << value; return ss.str(); } // Generate an array from start:step:end and pad each element to the specified length vector<string>generatePaddedArray(int start, int step, int end, int length) { vector<string>result; for (int i = start; i <= end; i += step) { result.push_back(padLeft(to_string(i), length)); } return result; } // Split a string by a delimiter vector<string>split(const string& s, char delimiter) { vector<string>tokens; string token; stringstream ss(s); while (getline(ss, token, delimiter)) { tokens.push_back(token); } return tokens; } // Helper function to trim spaces (only for spaces outside of quotes) string trim(const string& str) { size_t first = str.find_first_not_of(' '); size_t last = str.find_last_not_of(' '); if (first == string::npos || last == string::npos) return ""; return str.substr(first, (last - first + 1)); } // Helper function to check if a string is quoted bool isQuoted(const string& s) { return s.front() == '"' && s.back() == '"'; } // Remove the surrounding quotes from a string if it's quoted string removeQuotes(const string& s) { if (isQuoted(s)) { return s.substr(1, s.length() - 2); // Remove the quotes } return s; } void assignVariable(const string& expression) { string trimmedExpression = trim(expression); // 去除两端的空白字符 size_t eqPos = trimmedExpression.find('='); if (eqPos == string::npos) { cerr << "Invalid assignment expression: " << expression << endl; return; } string name = trimmedExpression.substr(0, eqPos); string value = trimmedExpression.substr(eqPos + 1); // 处理带引号的字符串(支持空格和特殊字符) value = trim(value); // 去除值两端的空格 if (isQuoted(value)) { // 如果是引号包围的字符串,去掉引号并赋值 variables[name] = removeQuotes(value); // 只赋值引号中的内容 return; } // 处理数组访问或切片 size_t leftBracket = value.find('['); size_t rightBracket = value.find(']'); if (leftBracket != string::npos && rightBracket != string::npos && leftBracket < rightBracket) { string arrayName = value.substr(0, leftBracket); string indexOrRange = value.substr(leftBracket + 1, rightBracket - leftBracket - 1); if (variables.find(arrayName) == variables.end()) { cerr << "Variable not found: " << arrayName << endl; return; } vector<string>array = split(variables[arrayName], ','); // Element access if (isNumber(indexOrRange)) { int index = stoi(indexOrRange); if (index < 0 || index >= static_cast<int>(array.size())) { cerr << "Index out of bounds: " << index << endl; return; } variables[name] = array[index]; } // Slicing else { vector<string>rangeParts = split(indexOrRange, ':'); if (rangeParts.size() != 2 || !isNumber(rangeParts[0]) || !isNumber(rangeParts[1])) { cerr << "Invalid slicing range: " << indexOrRange << endl; return; } int start = stoi(rangeParts[0]); int end = stoi(rangeParts[1]); if (start < 0 || end >= static_cast<int>(array.size()) || start > end) { cerr << "Invalid slicing indices." << endl; return; } stringstream ss; for (int i = start; i <= end; ++i) { if (i > start) ss << ","; ss << array[i]; } variables[name] = ss.str(); } return; } // Check if the value contains '@' size_t atPos = value.find('@'); if (atPos != string::npos) { // Extract padding length and actual value string paddingLengthStr = value.substr(0, atPos); string remainingValue = value.substr(atPos + 1); // Check if the remaining value is an array definition if (remainingValue.find(':') != string::npos) { defineArrayVariable(name, remainingValue, stoi(paddingLengthStr)); } else { // Otherwise, it's a single value with padding if (!isNumber(paddingLengthStr)) { cerr << "Invalid padding length: " << paddingLengthStr << endl; return; } int paddingLength = stoi(paddingLengthStr); variables[name] = padLeft(remainingValue, paddingLength); } } else { // Check if the value is an array definition if (value.find(':') != string::npos) { defineArrayVariable(name, value); } else { // Assign variable as-is variables[name] = value; } } } void defineArrayVariable(const string& name, const string& range, int length = -1) { vector<string>parts = split(range, ':'); if (parts.size() < 2 || parts.size() > 3) { cerr << "Invalid range format for variable: " << name << endl; return; } if (!isNumber(parts[0]) || !isNumber(parts.back())) { cerr << "Invalid range values for variable: " << name << endl; return; } int start = stoi(parts[0]); int step = 1; // Default step int end = stoi(parts.back()); if (parts.size() == 3) { if (!isNumber(parts[1])) { cerr << "Invalid step value for variable: " << name << endl; return; } step = stoi(parts[1]); } vector<string>array; if (length != -1) { array = generatePaddedArray(start, step, end, length); } else { array = generatePaddedArray(start, step, end, 0); // No padding if no length specified } stringstream ss; for (size_t i = 0; i < array.size(); ++i) { if (i > 0) ss << ","; ss << array[i]; } variables[name] = ss.str(); } void concatenateVariables(const string& expression) { size_t eqPos = expression.find('='); size_t plusPos = expression.find('+'); if (eqPos == string::npos || plusPos == string::npos || plusPos < eqPos) { cerr << "Invalid concatenation expression: " << expression << endl; return; } string resultName = expression.substr(0, eqPos); string var1 = expression.substr(eqPos + 1, plusPos - eqPos - 1); string var2 = expression.substr(plusPos + 1); // Trim whitespace from variable names var1 = trim(var1); var2 = trim(var2); // Determine if var1 and var2 are arrays or strings bool isVar1Array = false; bool isVar2Array = false; if (variables.find(var1) != variables.end()) { if (variables[var1].find(',') != string::npos) { isVar1Array = true; } } else { cerr << "First variable not found: " << var1 << endl; return; } if (variables.find(var2) != variables.end()) { if (variables[var2].find(',') != string::npos) { isVar2Array = true; } } else { cerr << "Second variable not found: " << var2 << endl; return; } if (isVar1Array && isVar2Array) { // Both are arrays, concatenate element-wise if they have the same length vector<string>array1 = split(variables[var1], ','); vector<string>array2 = split(variables[var2], ','); if (array1.size() != array2.size()) { cerr << "Arrays must be of the same length for element-wise concatenation." << endl; return; } vector<string>result; for (size_t i = 0; i < array1.size(); ++i) { result.push_back(array1[i] + array2[i]); } stringstream ss; for (size_t i = 0; i < result.size(); ++i) { if (i > 0) ss << ","; ss << result[i]; } variables[resultName] = ss.str(); // Store the result } else if (isVar1Array) { // var1 is an array, var2 is a string vector<string>array1 = split(variables[var1], ','); string strVar2 = variables[var2]; vector<string>result; for (const auto& elem : array1) { result.push_back(elem + strVar2); } stringstream ss; for (size_t i = 0; i < result.size(); ++i) { if (i > 0) ss << ","; ss << result[i]; } variables[resultName] = ss.str(); // Store the result } else if (isVar2Array) { // var1 is a string, var2 is an array string strVar1 = variables[var1]; vector<string>array2 = split(variables[var2], ','); vector<string>result; for (const auto& elem : array2) { result.push_back(strVar1 + elem); } stringstream ss; for (size_t i = 0; i < result.size(); ++i) { if (i > 0) ss << ","; ss << result[i]; } variables[resultName] = ss.str(); // Store the result } else { // Both are strings, simple concatenation string strVar1 = variables[var1]; string strVar2 = variables[var2]; variables[resultName] = strVar1 + strVar2; } } void printVariable(const string& command) { size_t spacePos = command.find(' '); if (spacePos == string::npos || command.substr(0, spacePos) != "print") { cerr << "Invalid print command: " << command << endl; return; } string name = command.substr(spacePos + 1); // Extract variable name after "print " name.erase(remove_if(name.begin(), name.end(), [](unsigned char c){ return std::isspace(c); }), name.end()); // Remove extra spaces if (variables.find(name) != variables.end()) { cout << name << " = " << variables[name] << endl; } else { cerr << "Variable not found: " << name << endl; } } // Execute system commands from variables (exec function) void execCommand(const string& command) { size_t spacePos = command.find(' '); if (spacePos == string::npos || command.substr(0, spacePos) != "exec") { cerr << "Invalid exec command: " << command << endl; return; } string varName = command.substr(spacePos + 1); varName.erase(remove_if(varName.begin(), varName.end(), [](unsigned char c){ return std::isspace(c); }), varName.end()); if (variables.find(varName) != variables.end()) { string value = variables[varName]; vector<string>commands = split(value, ','); // Split the command string if it's an array #pragma omp parallel for for (size_t i = 0; i < commands.size(); ++i) { int result = std::system(commands[i].c_str()); if (result != 0) { cerr << "Command failed: " << commands[i] << endl; } } } else { cerr << "Variable not found: " << varName << endl; } } void executeFromFile(const string& filename) { ifstream file(filename); if (!file.is_open()) { cerr << "Could not open file: " << filename << endl; return; } string line; while (getline(file, line)) { // Ignore empty lines and comments line = line.substr(0, line.find('#')); // Remove comments if (line.empty()) { continue; } // Check for print, exec, and assignment if (line.find("print") == 0) { printVariable(line); } else if (line.find("exec") == 0) { execCommand(line); // Handle exec command } else if (line.find("+") != string::npos) { concatenateVariables(line); } else { assignVariable(line); } } file.close(); } }; int main(int argc, char *argv[]) { if(argc>1) { int numThreads = omp_get_num_procs(); if(argc==3) numThreads=atoi(argv[2]); omp_set_num_threads(numThreads); SavitarInterpreter interpreter; interpreter.executeFromFile(argv[1]); } else cout<<"Please specify the Savitar script."<<endl; return 0; }
Example
Example 1: Basic Variables and Padding
Demonstrates number assignment, padding, and sequence generation.
                            
copy
# Number assignment c = 12345 d = 5@789 # Padding e = 1:2:10 # Sequence generation g = 3@1:2:10 # Sequence + padding h = 3@1:365 # Year-day sequence + padding # Slicing and single element access i = e[2:4] # Slice elements from index 2 to 4 j = e[2] # Single element # Print outputs print c print d print e print g print h print i print j
Example 2: Array Operations and Broadcasting
Demonstrates array addition, array + padded array broadcasting, and scalar + array operations.
                            
copy
arr1 = 1:2:5 # [1,3,5] arr2 = 6:2:10 # [6,8,10] arr3 = 5@1:10 # Padded array [001,002,...,010] # Array operations k = arr1 + arr2 # Element-wise addition l = arr1 + arr3 # Array + padded array broadcasting m = d + arr3 # Scalar + array broadcasting n = c + d # Scalar addition o = j + n # Scalar addition # Print outputs print arr1 print arr2 print arr3 print k print l print m print n print o
Example 3: String Concatenation and Parallel Execution
Demonstrates URL generation and executing a system command in parallel.
                            
copy
get = "wget ftps://gdc.cddis.eosdis.nasa.gov/gnss/products/ionex/2016/" doy = 3@1:365 # Padded day-of-year aff = "/codg" fix = "0.16i.Z" # String concatenation get1 = get + doy get2 = get1 + aff get3 = get2 + doy get4 = get3 + fix # Print and execute print get4 exec get4 # Execute system command
ROADMAP

202X

In the coming years, as AI continues to advance and become more powerful, the upgrades of Savitar will still rely on AI to accomplish.

2024

During some free time, I suddenly thought: why not try using a large language model to help implement it? It might actually work. After experimenting with multiple AIs and carefully fine-tuning through 23 versions, I had basically realized my original vision for Savitar.

2020

The TCC compiler I had been following had already gone through several updates, and I couldn’t help but wonder: when will my language finally be realized?

2019

I was still contemplating the challenge of developing a programming language entirely from scratch, which is extremely difficult. I began with syntax and lexical analysis and building an interpreter, using C as a transitional step, and then gradually refined the compiler, ultimately aiming to achieve language bootstrapping.

2017

I designed three syntax schemes for the Savitar language, primarily aimed at omitting explicit variable types. This does not mean types are absent; rather, variable types are indicated through capitalization: all lowercase for numeric variables, all uppercase for constants, and initial uppercase for character variables.

2016

I began considering the design of a new programming language and explored lexical and syntax analysis tools such as lex, yacc, flex, and bison, but found them quite complex. I wanted to create a language for batch processing, somewhat like bash, but with a simpler and more convenient syntax.