add vscodium / vscode and vim syntax highlight
8 files modified
16 files added
| New file |
| | |
| | | au BufRead,BufNewFile *.vs,*.vscript set filetype=voidscript |
| New file |
| | |
| | | " Advanced VoidScript indent |
| | | if exists("b:did_indent") |
| | | finish |
| | | endif |
| | | let b:did_indent = 1 |
| | | |
| | | setlocal indentexpr=GetVoidScriptIndent() |
| | | setlocal indentkeys=o,O,0},0),=else,=elif |
| | | |
| | | function! GetVoidScriptIndent() |
| | | let lnum = prevnonblank(v:lnum - 1) |
| | | if lnum == 0 |
| | | return 0 |
| | | endif |
| | | |
| | | let prevline = getline(lnum) |
| | | let currline = getline(v:lnum) |
| | | |
| | | " Strip comments |
| | | let prev = substitute(prevline, '#.*$', '', '') |
| | | let prev = substitute(prev, '//.*$', '', '') |
| | | let curr = substitute(currline, '#.*$', '', '') |
| | | let curr = substitute(curr, '//.*$', '', '') |
| | | |
| | | let indent = indent(lnum) |
| | | |
| | | " De-indent for lines that start with } or ) |
| | | if curr =~ '^\s*[\]\)}]' |
| | | return indent - &shiftwidth |
| | | endif |
| | | |
| | | " De-indent for else or elif |
| | | if curr =~ '^\s*\(else\|elif\)\>' |
| | | return indent - &shiftwidth |
| | | endif |
| | | |
| | | " Object inline literals: ignore increase if likely inline |
| | | if prev =~ '^\s*object\s\+\$\?\w\+\s*=\s*{' |
| | | return indent |
| | | endif |
| | | |
| | | " Increase indent after these block starters |
| | | if prev =~ '\v<((if|else|elif|for|while|function|class|object)\b.*)?{[ \t]*$' |
| | | return indent + &shiftwidth |
| | | endif |
| | | |
| | | " Increase indent for lines ending with just a { |
| | | if prev =~ '{\s*$' |
| | | return indent + &shiftwidth |
| | | endif |
| | | |
| | | " Keep same indent |
| | | return indent |
| | | endfunction |
| New file |
| | |
| | | " Vim syntax file for VoidScript |
| | | " Save as ~/.vim/syntax/voidscript.vim |
| | | |
| | | if exists("b:current_syntax") |
| | | finish |
| | | endif |
| | | |
| | | |
| | | " --- Keywords --- |
| | | syntax keyword voidscriptKeyword const function return for if else new sizeof this |
| | | |
| | | " --- Access Modifiers --- |
| | | syntax keyword voidscriptAccess public private protected |
| | | |
| | | " --- Types --- |
| | | syntax keyword voidscriptType int string float double boolean object class void |
| | | |
| | | " --- Control values --- |
| | | syntax keyword voidscriptBoolean true false null |
| | | |
| | | " --- Variable Names --- |
| | | syntax match voidscriptVariable /\$\k\+/ |
| | | |
| | | " --- Class definitions --- |
| | | syntax match voidscriptClass /\<class\s\+\k\+\>/ |
| | | |
| | | " --- Object property access (this->something or object->key) --- |
| | | syntax match voidscriptObjectAccess /\<this\>\|->/ |
| | | |
| | | " --- Function calls (e.g. func(...)) --- |
| | | syntax match voidscriptFunction /\<\k\+\s*(/ containedin=ALL |
| | | |
| | | " --- Numbers --- |
| | | syntax match voidscriptNumber /\<\d\+\(\.\d\+\)\?\>/ |
| | | |
| | | " --- Strings with variable highlighting --- |
| | | syntax region voidscriptString start=/"/ skip=/\\"/ end=/"/ contains=voidscriptVariable |
| | | |
| | | " --- Object literal keys (key: value) --- |
| | | syntax match voidscriptObjectKey /\<\k\+\>\s*:/ |
| | | |
| | | " Comments |
| | | syntax match voidscriptComment "#.*" |
| | | syntax match voidscriptComment "\/\/.*" contains=voidscriptTodo |
| | | highlight link voidscriptComment Comment |
| | | |
| | | " Optional: highlight TODO, FIXME, NOTE in comments |
| | | syntax match voidscriptTodo "\(TODO\|FIXME\|NOTE\):" contained |
| | | highlight link voidscriptTodo Todo |
| | | |
| | | |
| | | " --- Operators --- |
| | | syntax match voidscriptOperator /==\|!=\|<=\|>=\|[-+*/%<>=!&|]/ |
| | | |
| | | " --- Braces & Delimiters --- |
| | | syntax match voidscriptBraces /[{}[\]()]/ |
| | | syntax match voidscriptArrow /->/ |
| | | |
| | | " --- Highlight groups --- |
| | | highlight link voidscriptKeyword Keyword |
| | | highlight link voidscriptAccess StorageClass |
| | | highlight link voidscriptType Type |
| | | highlight link voidscriptBoolean Boolean |
| | | highlight link voidscriptVariable Identifier |
| | | highlight link voidscriptFunction Function |
| | | highlight link voidscriptNumber Number |
| | | highlight link voidscriptString String |
| | | highlight link voidscriptComment Comment |
| | | highlight link voidscriptTodo Todo |
| | | highlight link voidscriptOperator Operator |
| | | highlight link voidscriptBraces Delimiter |
| | | highlight link voidscriptArrow Operator |
| | | highlight link voidscriptClass Structure |
| | | highlight link voidscriptObjectAccess Operator |
| | | highlight link voidscriptObjectKey Identifier |
| | | |
| | | let b:current_syntax = "voidscript" |
| New file |
| | |
| | | # Node.js |
| | | node_modules/ |
| | | .vscode/ |
| | | *.vsix |
| | | .idea/ |
| | | *.sublime-workspace |
| | | *.sublime-project |
| | | .DS_Store |
| | | Thumbs.db |
| | | package-lock.json |
| | | .vscode/settings.json |
| New file |
| | |
| | | MIT License |
| | | |
| | | Copyright (c) 2025 fszontagh |
| | | |
| | | Permission is hereby granted, free of charge, to any person obtaining a copy |
| | | of this software and associated documentation files (the "Software"), to deal |
| | | in the Software without restriction, including without limitation the rights |
| | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| | | copies of the Software, and to permit persons to whom the Software is |
| | | furnished to do so, subject to the following conditions: |
| | | |
| | | The above copyright notice and this permission notice shall be included in all |
| | | copies or substantial portions of the Software. |
| | | |
| | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| | | SOFTWARE. |
| New file |
| | |
| | | # VoidScript Syntax Highlighting |
| | | |
| | | This Visual Studio Code extension provides syntax highlighting for the [VoidScript](https://github.com/fszontagh/voidscript) scripting language. |
| | | |
| | | ## Features |
| | | |
| | | - Syntax highlighting for VoidScript source files with extensions `.vs` and `.voidscript`. |
| | | |
| | | ## Requirements |
| | | |
| | | - Visual Studio Code version 1.58.0 or higher or compatible VSCodium |
| | | |
| | | ## Installation |
| | | |
| | | ### From Visual Studio Marketplace |
| | | |
| | | 1. Open the Extensions view (`Ctrl+Shift+X` / `Cmd+Shift+X` on macOS). |
| | | 2. Search for **VoidScript Syntax**. |
| | | 3. Click **Install**. |
| | | |
| | | ### From VSIX Package |
| | | |
| | | 1. Download the `voidscript-syntax-<version>.vsix` file from the [Releases](https://github.com/fszontagh/voidscript/releases) page. |
| | | 2. In VS Code, open the Command Palette (`Ctrl+Shift+P`), then select **Extensions: Install from VSIX...**. |
| | | 3. Choose the downloaded `.vsix` file. |
| | | |
| | | ## Development & Building |
| | | |
| | | To build and package the extension from source: |
| | | |
| | | ```bash |
| | | git clone https://github.com/fszontagh/voidscript.git |
| | | cd voidscript/assets/vscode/voidscript-syntax |
| | | npm install |
| | | npm run build |
| | | ``` |
| | | |
| | | After building, a `.vsix` package (e.g., `voidscript-syntax-1.0.0.vsix`) will be generated in the current directory. |
| | | |
| | | To install the extension from file: `code --install-extension voidscript-syntax-1.0.0.vsix` |
| | | |
| | | ## Usage |
| | | |
| | | Open a VoidScript file (extension `.vs` or `.voidscript`) in VS Code. The syntax highlighting will be applied automatically. |
| | | |
| | | ## Contributing |
| | | |
| | | Contributions to the grammar and syntax definitions are welcome! |
| | | |
| | | 1. Fork the repository and create a new branch. |
| | | 2. Make changes under `src/grammar/`. |
| | | 3. Commit and push your changes. |
| | | 4. Open a Pull Request. |
| | | |
| | | ## License |
| | | |
| | | MIT License. See the [LICENSE](LICENSE) file for details. |
| New file |
| | |
| | | #!/bin/bash |
| | | |
| | | # Check if 'vsce' (Visual Studio Code Extension Manager) is installed |
| | | if ! command -v vsce &> /dev/null |
| | | then |
| | | echo "'vsce' not found! Please install it to proceed." |
| | | echo "Install with: npm install -g vsce" |
| | | exit 1 |
| | | fi |
| | | |
| | | # Check if the package.json file exists |
| | | if [ ! -f "package.json" ]; then |
| | | echo "package.json not found. Are you running this script in the extension's root directory?" |
| | | exit 1 |
| | | fi |
| | | |
| | | # Start packaging the extension |
| | | echo "Packaging extension..." |
| | | |
| | | vsce package |
| | | |
| | | # If packaging succeeded, inform the user |
| | | if [ $? -eq 0 ]; then |
| | | echo "Extension package created successfully!" |
| | | else |
| | | echo "Error: Packaging failed." |
| | | exit 1 |
| | | fi |
| New file |
| | |
| | | { |
| | | "name": "voidscript-syntax", |
| | | "displayName": "VoidScript Syntax", |
| | | "description": "Syntax highlighting for VoidScript language.", |
| | | "license": "MIT", |
| | | "version": "1.0.0", |
| | | "publisher": "fszontagh", |
| | | "repository": { |
| | | "type": "git", |
| | | "url": "https://github.com/fszontagh/voidscript.git" |
| | | }, |
| | | "engines": { |
| | | "vscode": "^1.58.0" |
| | | }, |
| | | "contributes": { |
| | | "languages": [ |
| | | { |
| | | "id": "voidscript", |
| | | "extensions": [".voidscript", ".vs"], |
| | | "aliases": ["VoidScript"], |
| | | "configuration": "./src/grammar/language-configuration.json", |
| | | "mimetypes": ["application/x-voidscript", "application/vpodscript"] |
| | | } |
| | | ], |
| | | "grammars": [ |
| | | { |
| | | "language": "voidscript", |
| | | "scopeName": "source.voidscript", |
| | | "path": "./src/grammar/voidscript.tmLanguage.json" |
| | | } |
| | | ] |
| | | }, |
| | | "scripts": { |
| | | "vscode:prepublish": "npm install", |
| | | "compile": "echo \"No compile needed\"", |
| | | "create:package": "vsce package", |
| | | "build": "npm run vscode:prepublish; npm run create:package" |
| | | }, |
| | | "devDependencies": { |
| | | "typescript": "^4.0.0", |
| | | "vscode": "^1.1.37", |
| | | "eslint": "^7.23.0" |
| | | } |
| | | } |
| New file |
| | |
| | | { |
| | | "comments": { |
| | | "lineComment": "#", |
| | | "blockComment": [ |
| | | "/*", |
| | | "*/" |
| | | ] |
| | | }, |
| | | "brackets": [ |
| | | [ |
| | | "{", |
| | | "}" |
| | | ], |
| | | [ |
| | | "[", |
| | | "]" |
| | | ], |
| | | [ |
| | | "(", |
| | | ")" |
| | | ] |
| | | ], |
| | | "surroundingPairs": [ |
| | | [ |
| | | "{", |
| | | "}" |
| | | ], |
| | | [ |
| | | "[", |
| | | "]" |
| | | ], |
| | | [ |
| | | "(", |
| | | ")" |
| | | ], |
| | | [ |
| | | "'", |
| | | "'" |
| | | ], |
| | | [ |
| | | "\"", |
| | | "\"" |
| | | ] |
| | | ], |
| | | "autoClosingPairs": [ |
| | | { |
| | | "open": "(", |
| | | "close": ")", |
| | | "notIn": [ |
| | | "string" |
| | | ] |
| | | }, |
| | | { |
| | | "open": "\"", |
| | | "close": "\"", |
| | | "notIn": [ |
| | | "string" |
| | | ] |
| | | }, |
| | | { |
| | | "open": "'", |
| | | "close": "'", |
| | | "notIn": [ |
| | | "string" |
| | | ] |
| | | }, |
| | | { |
| | | "open": "{", |
| | | "close": "}", |
| | | "notIn": [ |
| | | "string" |
| | | ] |
| | | }, |
| | | { |
| | | "open": "[", |
| | | "close": "]", |
| | | "notIn": [ |
| | | "string" |
| | | ] |
| | | }, |
| | | ] |
| | | } |
| New file |
| | |
| | | { |
| | | "fileTypes": ["voidscript", "vs"], |
| | | "scopeName": "source.voidscript", |
| | | "patterns": [ |
| | | { |
| | | "name": "comment.line.number-sign.voidscript", |
| | | "match": "#.*" |
| | | }, |
| | | { |
| | | "name": "comment.line.double-slash.voidscript", |
| | | "match": "//.*" |
| | | }, |
| | | { |
| | | "name": "keyword.control.voidscript", |
| | | "match": "\\b(function|class|const|object|string|int|boolean|double|float|if|else|for|while|switch|case|break|continue|new|return|throw|typeof)\\b" |
| | | }, |
| | | { |
| | | "name": "variable.other.local.voidscript", |
| | | "match": "\\$[a-zA-Z_][a-zA-Z0-9_]*" |
| | | }, |
| | | { |
| | | "name": "variable.language.this.voidscript", |
| | | "match": "\\bthis\\b" |
| | | }, |
| | | { |
| | | "name": "string.quoted.double.voidscript", |
| | | "match": "\"([^\"]|\\\\\")*\"" |
| | | }, |
| | | { |
| | | "name": "constant.numeric.voidscript", |
| | | "match": "\\b\\d+(?:\\.\\d+)?\\b" |
| | | }, |
| | | { |
| | | "name": "constant.language.boolean.voidscript", |
| | | "match": "\\b(true|false)\\b" |
| | | }, |
| | | { |
| | | "name": "keyword.operator.voidscript", |
| | | "match": "(->|\\+=|-=|\\*=|\\/=|%=|&&|\\|\\||==|!=|>=|<=|>|<|\\+|-|\\*|\\/|%|=|!)" |
| | | } |
| | | ], |
| | | "repository": {} |
| | | } |
| New file |
| | |
| | | { |
| | | "compilerOptions": { |
| | | "target": "ES6", |
| | | "module": "commonjs", |
| | | "outDir": "./out", |
| | | "rootDir": "./src", |
| | | "strict": true |
| | | }, |
| | | "include": [ |
| | | "src/**/*" |
| | | ] |
| | | } |
| | |
| | | #include <filesystem> |
| | | #include <iostream> |
| | | #include <unordered_map> |
| | | #include <unistd.h> // for isatty, STDIN_FILENO |
| | | |
| | | #include "options.h" |
| | | #include "VoidScript.hpp" |
| | |
| | | for (const auto & [key, value] : params) { |
| | | usage.append(" [" + key + "]"); |
| | | } |
| | | // [file] is optional; if omitted, script is read from stdin |
| | | usage.append(" [file]"); |
| | | // Parse arguments: allow --help, --version, --debug[=component], and a single file |
| | | bool debugLexer = false; |
| | | bool debugParser = false; |
| | |
| | | std::cerr << usage << "\n"; |
| | | return 1; |
| | | } |
| | | } else if (a == "-") { |
| | | // Read script from stdin |
| | | file = a; |
| | | } else if (a.starts_with("-")) { |
| | | std::cerr << "Error: Unknown option '" << a << "'\n"; |
| | | std::cerr << usage << "\n"; |
| | |
| | | } |
| | | } |
| | | if (file.empty()) { |
| | | std::cerr << "Error: No input file specified\n"; |
| | | // No input file specified: read script from stdin |
| | | file = "-"; |
| | | } |
| | | // If reading from stdin but stdin is a tty (no piped input), show usage |
| | | if (file == "-" && isatty(STDIN_FILENO)) { |
| | | std::cerr << usage << "\n"; |
| | | return 1; |
| | | } |
| | | |
| | | if (!std::filesystem::exists(file)) { |
| | | std::cerr << "Error: File " << file << " does not exist.\n"; |
| | | return 1; |
| | | // Determine if reading from a file or stdin |
| | | std::string filename; |
| | | if (file == "-") { |
| | | // Read script from standard input |
| | | filename = file; |
| | | } else { |
| | | if (!std::filesystem::exists(file)) { |
| | | std::cerr << "Error: File " << file << " does not exist.\n"; |
| | | return 1; |
| | | } |
| | | filename = std::filesystem::canonical(file).string(); |
| | | } |
| | | |
| | | const std::string filename = std::filesystem::canonical(file).string(); |
| | | |
| | | // Initialize and run with debug options |
| | | VoidScript voidscript(filename, debugLexer, debugParser, debugInterp, debugSymbolTable); |
| | |
| | | /** |
| | | * @brief Expression node representing a function call returning a value. |
| | | */ |
| | | class CallExpressionNode : public ExpressionNode { |
| | | std::string functionName_; |
| | | std::vector<std::unique_ptr<ExpressionNode>> args_; |
| | | class CallExpressionNode : public ExpressionNode { |
| | | std::string functionName_; |
| | | std::vector<std::unique_ptr<ExpressionNode>> args_; |
| | | // Source location for error reporting |
| | | std::string filename_; |
| | | int line_; |
| | | size_t column_; |
| | | |
| | | public: |
| | | CallExpressionNode(std::string functionName, |
| | | std::vector<std::unique_ptr<ExpressionNode>> args) : |
| | | functionName_(std::move(functionName)), args_(std::move(args)) {} |
| | | std::vector<std::unique_ptr<ExpressionNode>> args, |
| | | const std::string & filename, |
| | | int line, size_t column) : |
| | | functionName_(std::move(functionName)), args_(std::move(args)), |
| | | filename_(filename), line_(line), column_(column) {} |
| | | |
| | | Symbols::Value evaluate(Interpreter &interpreter) const override { |
| | | using namespace Symbols; |
| | | // Evaluate argument expressions |
| | | std::vector<Value> argValues; |
| | | argValues.reserve(args_.size()); |
| | | for (const auto &expr : args_) { |
| | | argValues.push_back(expr->evaluate(interpreter)); |
| | | } |
| | | try { |
| | | // Evaluate argument expressions |
| | | std::vector<Value> argValues; |
| | | argValues.reserve(args_.size()); |
| | | for (const auto &expr : args_) { |
| | | argValues.push_back(expr->evaluate(interpreter)); |
| | | } |
| | | |
| | | // Built-in function |
| | | auto &mgr = Modules::ModuleManager::instance(); |
| | | if (mgr.hasFunction(functionName_)) { |
| | | return mgr.callFunction(functionName_, argValues); |
| | | } |
| | | // Built-in function |
| | | auto &mgr = Modules::ModuleManager::instance(); |
| | | if (mgr.hasFunction(functionName_)) { |
| | | return mgr.callFunction(functionName_, argValues); |
| | | } |
| | | |
| | | // User-defined function: lookup through scope hierarchy |
| | | SymbolContainer *sc = SymbolContainer::instance(); |
| | | std::string lookupNs = sc->currentScopeName(); |
| | | std::shared_ptr<FunctionSymbol> funcSym; |
| | | // Search for function symbol in current and parent scopes |
| | | while (true) { |
| | | std::string fnSymNs = lookupNs + ".functions"; |
| | | auto sym = sc->get(fnSymNs, functionName_); |
| | | if (sym && sym->getKind() == Kind::Function) { |
| | | funcSym = std::static_pointer_cast<FunctionSymbol>(sym); |
| | | break; |
| | | } |
| | | auto pos = lookupNs.find_last_of('.'); |
| | | if (pos == std::string::npos) { |
| | | break; |
| | | } |
| | | lookupNs = lookupNs.substr(0, pos); |
| | | } |
| | | if (!funcSym) { |
| | | throw std::runtime_error("Function not found: " + functionName_); |
| | | } |
| | | const auto ¶ms = funcSym->parameters(); |
| | | if (params.size() != argValues.size()) { |
| | | throw std::runtime_error( |
| | | "Function '" + functionName_ + "' expects " + std::to_string(params.size()) + |
| | | " args, got " + std::to_string(argValues.size())); |
| | | } |
| | | // Enter function scope and bind parameters |
| | | const std::string fnOpNs = funcSym->context() + "." + functionName_; |
| | | sc->enter(fnOpNs); |
| | | for (size_t i = 0; i < params.size(); ++i) { |
| | | const auto &p = params[i]; |
| | | const Value &v = argValues[i]; |
| | | auto varSym = SymbolFactory::createVariable(p.name, v, fnOpNs); |
| | | sc->add(varSym); |
| | | } |
| | | // Execute function body operations and capture return |
| | | Symbols::Value returnValue; |
| | | auto ops = Operations::Container::instance()->getAll(fnOpNs); |
| | | for (const auto &op : ops) { |
| | | try { |
| | | interpreter.runOperation(*op); |
| | | } catch (const ReturnException &ret) { |
| | | returnValue = ret.value(); |
| | | break; |
| | | } |
| | | } |
| | | sc->enterPreviousScope(); |
| | | return returnValue; |
| | | // User-defined function: lookup through scope hierarchy |
| | | SymbolContainer *sc = SymbolContainer::instance(); |
| | | std::string lookupNs = sc->currentScopeName(); |
| | | std::shared_ptr<FunctionSymbol> funcSym; |
| | | // Search for function symbol in current and parent scopes |
| | | while (true) { |
| | | std::string fnSymNs = lookupNs + ".functions"; |
| | | auto sym = sc->get(fnSymNs, functionName_); |
| | | if (sym && sym->getKind() == Kind::Function) { |
| | | funcSym = std::static_pointer_cast<FunctionSymbol>(sym); |
| | | break; |
| | | } |
| | | auto pos = lookupNs.find_last_of('.'); |
| | | if (pos == std::string::npos) { |
| | | break; |
| | | } |
| | | lookupNs = lookupNs.substr(0, pos); |
| | | } |
| | | if (!funcSym) { |
| | | throw std::runtime_error("Function not found: " + functionName_); |
| | | } |
| | | const auto ¶ms = funcSym->parameters(); |
| | | if (params.size() != argValues.size()) { |
| | | throw std::runtime_error( |
| | | "Function '" + functionName_ + "' expects " + std::to_string(params.size()) + |
| | | " args, got " + std::to_string(argValues.size())); |
| | | } |
| | | // Enter function scope and bind parameters |
| | | const std::string fnOpNs = funcSym->context() + "." + functionName_; |
| | | sc->enter(fnOpNs); |
| | | for (size_t i = 0; i < params.size(); ++i) { |
| | | const auto &p = params[i]; |
| | | const Value &v = argValues[i]; |
| | | auto varSym = SymbolFactory::createVariable(p.name, v, fnOpNs); |
| | | sc->add(varSym); |
| | | } |
| | | // Execute function body operations and capture return |
| | | Symbols::Value returnValue; |
| | | auto ops = Operations::Container::instance()->getAll(fnOpNs); |
| | | for (const auto &op : ops) { |
| | | try { |
| | | interpreter.runOperation(*op); |
| | | } catch (const ReturnException &ret) { |
| | | returnValue = ret.value(); |
| | | break; |
| | | } |
| | | } |
| | | sc->enterPreviousScope(); |
| | | return returnValue; |
| | | } catch (const std::exception &e) { |
| | | throw ::Interpreter::Exception(e.what(), filename_, line_, column_); |
| | | } |
| | | // Unreachable: all paths either return or throw |
| | | return Symbols::Value(); |
| | | } |
| | | |
| | | std::string toString() const override { |
| | |
| | | for (const auto &arg : expr->args) { |
| | | callArgs.push_back(buildExpressionFromParsed(arg)); |
| | | } |
| | | return std::make_unique<Interpreter::CallExpressionNode>(expr->name, std::move(callArgs)); |
| | | // Create call node with source location |
| | | return std::make_unique<Interpreter::CallExpressionNode>(expr->name, |
| | | std::move(callArgs), expr->filename, expr->line, expr->column); |
| | | } |
| | | case Kind::Object: |
| | | { |
| | |
| | | #define MODULES_BASEMODULE_HPP |
| | | |
| | | |
| | | // Base exception type for module errors |
| | | #include "../BaseException.hpp" |
| | | namespace Modules { |
| | | |
| | | /** |
| | |
| | | virtual void registerModule() = 0; |
| | | }; |
| | | |
| | | /** |
| | | * @brief Exception type for errors thrown within module functions. |
| | | * Inherit from BaseException to allow rich error messages. |
| | | */ |
| | | class Exception : public ::BaseException { |
| | | public: |
| | | /** |
| | | * Construct a module exception with a message. |
| | | * @param msg Error message |
| | | */ |
| | | explicit Exception(const std::string & msg) |
| | | : BaseException(msg) {} |
| | | }; |
| | | |
| | | } // namespace Modules |
| | | #endif // MODULES_BASEMODULE_HPP |
| | |
| | | std::cerr << "\n"; |
| | | return Symbols::Value(); |
| | | }); |
| | | // Built-in error thrower: throws a module exception with provided message |
| | | mgr.registerFunction("throw_error", [](const std::vector<Symbols::Value> & args) { |
| | | if (args.size() != 1 || args[0].getType() != Symbols::Variables::Type::STRING) { |
| | | throw Exception("throw_error requires exactly one string argument"); |
| | | } |
| | | std::string msg = args[0].get<std::string>(); |
| | | throw Exception(msg); |
| | | return Symbols::Value(); // never reached |
| | | }); |
| | | } |
| | | }; |
| | | |
| | |
| | | // For function call arguments |
| | | std::vector<ParsedExpressionPtr> args; |
| | | std::vector<std::pair<std::string, ParsedExpressionPtr>> objectMembers; |
| | | // Source location for error reporting |
| | | std::string filename; |
| | | int line = 0; |
| | | size_t column = 0; |
| | | |
| | | // Constructor for literal |
| | | static ParsedExpressionPtr makeLiteral(const Symbols::Value & val) { |
| | |
| | | } |
| | | } |
| | | expect(Lexer::Tokens::Type::PUNCTUATION, ")"); |
| | | // Create call expression node |
| | | output_queue.push_back(ParsedExpression::makeCall(func_name, std::move(call_args))); |
| | | // Create call expression node with source location |
| | | { |
| | | auto pe = ParsedExpression::makeCall(func_name, std::move(call_args)); |
| | | pe->filename = this->current_filename_; |
| | | pe->line = token.line_number; |
| | | pe->column = token.column_number; |
| | | output_queue.push_back(std::move(pe)); |
| | | } |
| | | expect_unary = false; |
| | | } else if (token.type == Lexer::Tokens::Type::OPERATOR_ARITHMETIC || |
| | | token.type == Lexer::Tokens::Type::OPERATOR_RELATIONAL || |
| | |
| | | #include <filesystem> |
| | | #include <fstream> |
| | | #include <string> |
| | | #include <iostream> |
| | | #include <iterator> |
| | | |
| | | #include "Interpreter/Interpreter.hpp" |
| | | #include "Lexer/Lexer.hpp" |
| | |
| | | std::shared_ptr<Parser::Parser> parser = nullptr; |
| | | |
| | | static std::string readFile(const std::string & file) { |
| | | // Read from stdin if '-' is specified |
| | | if (file == "-") { |
| | | return std::string(std::istreambuf_iterator<char>(std::cin), std::istreambuf_iterator<char>()); |
| | | } |
| | | if (!std::filesystem::exists(file)) { |
| | | throw std::runtime_error("File " + file + " does not exits"); |
| | | throw std::runtime_error("File " + file + " does not exist"); |
| | | } |
| | | std::ifstream input(file, std::ios::in); |
| | | if (!input.is_open()) { |
| | | throw std::runtime_error("Could not open file " + file); |
| | | return ""; |
| | | } |
| | | std::string content = std::string((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>()); |
| | | std::string content((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>()); |
| | | input.close(); |
| | | return content; |
| | | } |
| New file |
| | |
| | | // Test script for throw_error built-in function |
| | | throw_error("Oops here"); |