| #include <math.h> | |||||
| #include <stdio.h> | |||||
| //#bb ldflags := -lm | |||||
| //#bb pkgs := libsdl cairo | |||||
| int main() { | |||||
| printf("sin(PI) is %f\n", sin(M_PI)); | |||||
| } |
| std::string str; | std::string str; | ||||
| switch (peek()) { | switch (peek()) { | ||||
| case '{': | |||||
| skip(); | |||||
| parseString(vars, str, '}'); | |||||
| skip('}'); | |||||
| appendVariableToArray(vars, str, values); | |||||
| break; | |||||
| default: | |||||
| if (!parseIdentifier(str)) { | |||||
| error("No identifier after $."); | |||||
| } | |||||
| case '{': | |||||
| skip(); | |||||
| parseString(vars, str, '}'); | |||||
| skip('}'); | |||||
| appendVariableToArray(vars, str, values); | |||||
| break; | |||||
| default: | |||||
| if (!parseIdentifier(str)) { | |||||
| error("No identifier after $."); | |||||
| } | |||||
| appendVariableToArray(vars, str, values); | |||||
| break; | |||||
| appendVariableToArray(vars, str, values); | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| std::string str; | std::string str; | ||||
| switch (peek()) { | switch (peek()) { | ||||
| case '{': | |||||
| skip(); | |||||
| parseString(vars, str, '}'); | |||||
| skip('}'); | |||||
| appendVariableToString(vars, str, content); | |||||
| break; | |||||
| default: | |||||
| if (!parseIdentifier(str)) { | |||||
| error("No identifier after $."); | |||||
| } | |||||
| case '{': | |||||
| skip(); | |||||
| parseString(vars, str, '}'); | |||||
| skip('}'); | |||||
| appendVariableToString(vars, str, content); | |||||
| break; | |||||
| appendVariableToString(vars, str, content); | |||||
| break; | |||||
| default: | |||||
| if (!parseIdentifier(str)) { | |||||
| error("No identifier after $."); | |||||
| } | |||||
| appendVariableToString(vars, str, content); | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| int ch; | int ch; | ||||
| while ((ch = peek()) != EOF) { | while ((ch = peek()) != EOF) { | ||||
| switch (ch) { | switch (ch) { | ||||
| case EOF: | |||||
| error("Unexpected EOF"); | |||||
| case EOF: | |||||
| error("Unexpected EOF"); | |||||
| case '\\': | |||||
| content.push_back(parseEscape()); | |||||
| break; | |||||
| case '\\': | |||||
| content.push_back(parseEscape()); | |||||
| break; | |||||
| case '$': | |||||
| parseQuotedExpansion(vars, content); | |||||
| break; | |||||
| case '$': | |||||
| parseQuotedExpansion(vars, content); | |||||
| break; | |||||
| case '"': | |||||
| skip(); | |||||
| return; | |||||
| case '"': | |||||
| skip(); | |||||
| return; | |||||
| default: | |||||
| content.push_back(get()); | |||||
| break; | |||||
| default: | |||||
| content.push_back(get()); | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| switch (ch) { | switch (ch) { | ||||
| case EOF: | |||||
| return success; | |||||
| case EOF: | |||||
| return success; | |||||
| case '\\': | |||||
| content.push_back(parseEscape()); | |||||
| success = true; | |||||
| break; | |||||
| case '\\': | |||||
| content.push_back(parseEscape()); | |||||
| success = true; | |||||
| break; | |||||
| case '$': | |||||
| parseQuotedExpansion(vars, content); | |||||
| success = true; | |||||
| break; | |||||
| case '$': | |||||
| parseQuotedExpansion(vars, content); | |||||
| success = true; | |||||
| break; | |||||
| case '"': | |||||
| parseQuotedString(vars, content); | |||||
| success = true; | |||||
| break; | |||||
| case '"': | |||||
| parseQuotedString(vars, content); | |||||
| success = true; | |||||
| break; | |||||
| default: | |||||
| if (ch == ':' && peek2() == '=') | |||||
| return success; | |||||
| default: | |||||
| if (ch == ':' && peek2() == '=') | |||||
| return success; | |||||
| content.push_back(get()); | |||||
| success = true; | |||||
| break; | |||||
| content.push_back(get()); | |||||
| success = true; | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| bool BBBParser::parseIdentifier(std::string &content) { | bool BBBParser::parseIdentifier(std::string &content) { | ||||
| int ch = peek(); | int ch = peek(); | ||||
| if (!( | if (!( | ||||
| (ch >= 'a' && ch <= 'z') || | |||||
| (ch >= 'A' && ch <= 'Z') || | |||||
| (ch == '_'))) { | |||||
| (ch >= 'a' && ch <= 'z') || | |||||
| (ch >= 'A' && ch <= 'Z') || | |||||
| (ch == '_'))) { | |||||
| return false; | return false; | ||||
| } | } | ||||
| while (1) { | while (1) { | ||||
| ch = peek(); | ch = peek(); | ||||
| if (!( | if (!( | ||||
| (ch >= '0' && ch <= '9') || | |||||
| (ch >= 'a' && ch <= 'z') || | |||||
| (ch >= 'A' && ch <= 'Z') || | |||||
| (ch == '_'))) { | |||||
| (ch >= '0' && ch <= '9') || | |||||
| (ch >= 'a' && ch <= 'z') || | |||||
| (ch >= 'A' && ch <= 'Z') || | |||||
| (ch == '_'))) { | |||||
| return true; | return true; | ||||
| } | } | ||||
| return; | return; | ||||
| } | } | ||||
| skipWhitespace(); | |||||
| Operator prevOper = readOperator(); | Operator prevOper = readOperator(); | ||||
| if (prevOper == Operator::NONE) { | if (prevOper == Operator::NONE) { | ||||
| error("Expected operator."); | error("Expected operator."); |
| void skip(char); | void skip(char); | ||||
| void skip() { get(); } | void skip() { get(); } | ||||
| int flags_; | |||||
| int line_; | |||||
| int ch_; | |||||
| int line() const { return line_; } | |||||
| int ch() const { return ch_; } | |||||
| private: | private: | ||||
| enum class Operator { | enum class Operator { | ||||
| bool parseString(const Variables &vars, std::string &content, int sep = -1); | bool parseString(const Variables &vars, std::string &content, int sep = -1); | ||||
| bool parseIdentifier(std::string &content); | bool parseIdentifier(std::string &content); | ||||
| int flags_; | |||||
| int line_; | |||||
| int ch_; | |||||
| std::istream &stream_; | std::istream &stream_; | ||||
| }; | }; |
| static bool startsWith(BBBParser &parser, const char *str) { | static bool startsWith(BBBParser &parser, const char *str) { | ||||
| for (size_t i = 0; str[i] != '\0'; ++i) { | for (size_t i = 0; str[i] != '\0'; ++i) { | ||||
| if (parser.get() != str[i]) | |||||
| if (parser.peek() != str[i]) | |||||
| return false; | return false; | ||||
| parser.skip(); | |||||
| } | } | ||||
| return true; | return true; | ||||
| std::ifstream file(dir_ + "/" + name_); | std::ifstream file(dir_ + "/" + name_); | ||||
| BBBParser parser(file, BBBParser::FLAG_ONE_LINE); | BBBParser parser(file, BBBParser::FLAG_ONE_LINE); | ||||
| bool freshLine = true; | |||||
| while (file.good()) { | while (file.good()) { | ||||
| if (freshLine && startsWith(parser, "//#bb")) { | |||||
| if (startsWith(parser, "//#bb")) { | |||||
| parser.parse(vars_); | parser.parse(vars_); | ||||
| } else { | } else { | ||||
| while (file.good() && parser.get() != '\n'); | while (file.good() && parser.get() != '\n'); | ||||
| freshLine = true; | |||||
| } | } | ||||
| } | } | ||||
| } | } |
| public: | public: | ||||
| SourceFile(std::string dir, std::string name, BBBParser::Variables vars); | SourceFile(std::string dir, std::string name, BBBParser::Variables vars); | ||||
| const std::string &dir() const { return dir_; } | |||||
| const std::string &name() const { return name_; } | |||||
| const BBBParser::Variables &vars() const { return vars_; } | |||||
| private: | private: | ||||
| std::string dir_; | std::string dir_; | ||||
| std::string name_; | std::string name_; |
| #include <sys/types.h> | #include <sys/types.h> | ||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||
| #include <unistd.h> | #include <unistd.h> | ||||
| #include <getopt.h> | |||||
| #include <dirent.h> | #include <dirent.h> | ||||
| #include <stdexcept> | #include <stdexcept> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include <errno.h> | #include <errno.h> | ||||
| #include <fstream> | #include <fstream> | ||||
| #include <iostream> | |||||
| #include "SourceFile.h" | #include "SourceFile.h" | ||||
| #include "BBBParser.h" | #include "BBBParser.h" | ||||
| void readDir(std::string dir, std::vector<SourceFile> &sources, BBBParser::Variables vars) { | |||||
| static void readDir(std::string dir, std::vector<SourceFile> &sources, | |||||
| BBBParser::Variables vars); | |||||
| static void readPath(std::string dir, std::string name, | |||||
| std::vector<SourceFile> &sources, BBBParser::Variables vars) { | |||||
| std::string path = dir + "/" + name; | |||||
| struct stat st; | |||||
| if (stat(path.c_str(), &st) < 0) { | |||||
| throw std::runtime_error("stat '" + path + "': " + strerror(errno)); | |||||
| } | |||||
| if (S_ISDIR(st.st_mode)) { | |||||
| // We don't want to send 'files' | |||||
| BBBParser::Variables subvars = vars; | |||||
| subvars.erase("files"); | |||||
| readDir(path, sources, std::move(subvars)); | |||||
| } else { | |||||
| sources.emplace_back(dir, name, vars); | |||||
| } | |||||
| } | |||||
| static void readDir(std::string dir, std::vector<SourceFile> &sources, | |||||
| BBBParser::Variables vars) { | |||||
| std::ifstream buildFile(dir + "/build.bbb"); | std::ifstream buildFile(dir + "/build.bbb"); | ||||
| std::vector<std::string> files; | std::vector<std::string> files; | ||||
| bool hasFiles = false; | bool hasFiles = false; | ||||
| struct dirent *ent; | struct dirent *ent; | ||||
| while ((ent = readdir(d)) != NULL) { | while ((ent = readdir(d)) != NULL) { | ||||
| if (ent->d_name[0] == '.') | |||||
| continue; | |||||
| files.emplace_back(ent->d_name); | files.emplace_back(ent->d_name); | ||||
| } | } | ||||
| // Go through files | // Go through files | ||||
| for (auto &ent: files) { | for (auto &ent: files) { | ||||
| std::string path = dir + "/" + ent; | std::string path = dir + "/" + ent; | ||||
| struct stat st; | |||||
| if (stat(path.c_str(), &st) < 0) { | |||||
| throw std::runtime_error("stat '" + path + "': " + strerror(errno)); | |||||
| } | |||||
| BBBParser::Variables subvars = vars; | |||||
| readPath(dir, ent, sources, vars); | |||||
| } | |||||
| } | |||||
| if (S_ISDIR(st.st_mode)) { | |||||
| readDir(path, sources, vars); | |||||
| } else { | |||||
| sources.emplace_back(dir, ent, vars); | |||||
| void printState(const std::vector<SourceFile> &sources) { | |||||
| for (auto &source: sources) { | |||||
| std::cout << source.dir() << '/' << source.name() << ":\n"; | |||||
| for (auto &kv: source.vars()) { | |||||
| std::cout << " " << kv.first << ":\n"; | |||||
| for (auto &val: kv.second) { | |||||
| std::cout << " " << val << '\n'; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||
| std::ifstream file("sample.bbb"); | |||||
| BBBParser parser(file, BBBParser::FLAG_NONE); | |||||
| std::vector<std::string> srcDirs; | |||||
| std::string outdir = "bbbuild"; | |||||
| enum class Action { | |||||
| BUILD, PRINT_STATE, | |||||
| }; | |||||
| Action action = Action::BUILD; | |||||
| BBBParser::Variables vars; | |||||
| parser.parse(vars); | |||||
| const char *shortopts = "o:hp"; | |||||
| const struct option opts[] = { | |||||
| { "help", no_argument, NULL, 'h' }, | |||||
| { "output", required_argument, NULL, 'o' }, | |||||
| { "print-state", no_argument, NULL, 'p' }, | |||||
| {}, | |||||
| }; | |||||
| std::cout << "\nVariables found:\n"; | |||||
| for (auto &kv: vars) { | |||||
| std::cout << kv.first << ":\n"; | |||||
| for (auto &val: kv.second) { | |||||
| std::cout << "\t'" << val << "'\n"; | |||||
| const char usage[] = | |||||
| "Usage: bbbuild [options...] [sources]\n" | |||||
| "\n" | |||||
| " -h, --help " | |||||
| "Show this help text.\n" | |||||
| " -o, --output <dir> " | |||||
| "Set output directory.\n" | |||||
| " -p, --print-state " | |||||
| "Print the state instead of building.\n"; | |||||
| // Parse options from argv | |||||
| while (1) { | |||||
| int optidx; | |||||
| int c = getopt_long(argc, argv, shortopts, opts, &optidx); | |||||
| if (c < 0) { | |||||
| break; | |||||
| } | } | ||||
| switch (c) { | |||||
| case 'h': | |||||
| puts(usage); | |||||
| return 0; | |||||
| case 'o': | |||||
| outdir = optarg; | |||||
| break; | |||||
| case 'p': | |||||
| action = Action::PRINT_STATE; | |||||
| break; | |||||
| default: | |||||
| printf("Unknown option: '%c'.\n%s", (char)c, usage); | |||||
| break; | |||||
| return 1; | |||||
| } | |||||
| } | |||||
| // Parse non-opt argv as source dirs | |||||
| if (optind < argc) { | |||||
| while (optind < argc) { | |||||
| srcDirs.push_back(argv[optind++]); | |||||
| } | |||||
| } else { | |||||
| srcDirs.push_back("."); | |||||
| } | |||||
| // Read configs from source dirs | |||||
| std::vector<SourceFile> sources; | |||||
| for (std::string &dir: srcDirs) { | |||||
| readDir(dir, sources, {}); | |||||
| } | |||||
| switch (action) { | |||||
| case Action::BUILD: | |||||
| std::cerr << "Building is not implemented yet.\n"; | |||||
| abort(); | |||||
| case Action::PRINT_STATE: | |||||
| printState(sources); | |||||
| break; | |||||
| } | } | ||||
| } | } |