diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f2a56e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd +*.pyw +*.pyz +*.pywz \ No newline at end of file diff --git a/README.md b/README.md index b3a9adc..e751bd3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ - - # Scripting Language Factory A powerful tool for creating, running, and managing custom programming languages based on any slang, meme terminology, or domain-specific vocabulary you prefer. @@ -90,6 +88,27 @@ python transpiler.py compile script.ski -o output_dir -k -c my_mapping.json Compile your custom language to Python bytecode (.pyc files). The `-k` flag keeps the intermediate Python file. +### Generate VS Code Extension + +``` +python transpiler.py -c my_mapping.json vscode -o my-language-extension +``` + +Generate a VS Code extension for syntax highlighting based on your custom language mapping. This creates all the necessary files for a complete VS Code extension, including: + +- Syntax highlighting based on your language keywords +- Language configuration for comments, brackets, and indentation +- Package manifest with language metadata + +To install the extension: +1. Copy the generated folder to your VS Code extensions directory: + - Windows: `%USERPROFILE%\.vscode\extensions` + - macOS/Linux: `~/.vscode/extensions` +2. Restart VS Code +3. Open a file with your language's extension to see the highlighting in action + +NOTE: Advanced auto-completion is not yet supported, but basic auto-completion is available. + ## Customizing Your Language Edit the mapping file to define your own language syntax. The file has a structured format: diff --git a/rizzlang_ext/README.md b/rizzlang_ext/README.md new file mode 100644 index 0000000..cd51e13 --- /dev/null +++ b/rizzlang_ext/README.md @@ -0,0 +1,3 @@ +# RizzLang VS Code Extension + +This extension provides syntax highlighting for RizzLang files. diff --git a/rizzlang_ext/language-configuration.json b/rizzlang_ext/language-configuration.json new file mode 100644 index 0000000..a12efee --- /dev/null +++ b/rizzlang_ext/language-configuration.json @@ -0,0 +1,74 @@ +{ + "comments": { + "lineComment": "#" + }, + "brackets": [ + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] + ], + "autoClosingPairs": [ + { + "open": "{", + "close": "}" + }, + { + "open": "[", + "close": "]" + }, + { + "open": "(", + "close": ")" + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + } + ], + "surroundingPairs": [ + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ] + ], + "indentationRules": { + "increaseIndentPattern": "^.*:\\s*$", + "decreaseIndentPattern": "^\\s*$" + } +} \ No newline at end of file diff --git a/rizzlang_ext/package.json b/rizzlang_ext/package.json new file mode 100644 index 0000000..332a7b7 --- /dev/null +++ b/rizzlang_ext/package.json @@ -0,0 +1,33 @@ +{ + "name": "rizzlang", + "displayName": "RizzLang", + "description": "A meme-based programming language", + "version": "0.1.0", + "engines": { + "vscode": "^1.60.0" + }, + "categories": [ + "Programming Languages" + ], + "contributes": { + "languages": [ + { + "id": "rizzlang", + "aliases": [ + "RizzLang" + ], + "extensions": [ + "ski" + ], + "configuration": "./language-configuration.json" + } + ], + "grammars": [ + { + "language": "rizzlang", + "scopeName": "source.rizzlang", + "path": "./syntaxes/rizzlang.tmLanguage.json" + } + ] + } +} \ No newline at end of file diff --git a/rizzlang_ext/syntaxes/rizzlang.tmLanguage.json b/rizzlang_ext/syntaxes/rizzlang.tmLanguage.json new file mode 100644 index 0000000..47f83c5 --- /dev/null +++ b/rizzlang_ext/syntaxes/rizzlang.tmLanguage.json @@ -0,0 +1,128 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "RizzLang", + "patterns": [ + { + "include": "#keywords" + }, + { + "include": "#strings" + }, + { + "include": "#comments" + }, + { + "include": "#numbers" + }, + { + "include": "#function-call" + }, + { + "include": "#decorator" + } + ], + "repository": { + "keywords": { + "patterns": [ + { + "name": "keyword.control.rizzlang", + "match": "\\b(rizz|fr|yeet|sus|vibe_check|boomer|zoomer)\\b" + }, + { + "name": "keyword.declaration.rizzlang", + "match": "\\b(skibidi|toilet|ohio|based|stan|simp|slide into (\\w+))\\b" + }, + { + "name": "keyword.operator.rizzlang", + "match": "\\b(bet)\\b" + }, + { + "name": "constant.language.rizzlang", + "match": "\\b(no_cap|cap|mid|down_bad|up_good)\\b" + }, + { + "name": "support.function.rizzlang", + "match": "\\b(bussin|slay|sheesh|goated|npc|glizzy|drip|rent_free|chad|touch_grass|ong|main_character|villain_arc|gaslighting|gatekeeping|girlboss|sigma|alpha|beta|skill_issue|cope|seethe|mald|ratio (\\w+))\\b" + }, + { + "name": "storage.modifier.rizzlang", + "match": "\\b(lowkey|highkey|on god)\\b" + }, + { + "name": "keyword.control.exception.rizzlang", + "match": "\\b(finna|bruh|karen|cringe|plot_twist|L_plus_ratio|no shot|spill the tea)\\b" + } + ] + }, + "strings": { + "patterns": [ + { + "name": "string.quoted.double.rizzlang", + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape.rizzlang", + "match": "\\\\." + } + ] + }, + { + "name": "string.quoted.single.rizzlang", + "begin": "'", + "end": "'", + "patterns": [ + { + "name": "constant.character.escape.rizzlang", + "match": "\\\\." + } + ] + }, + { + "name": "string.quoted.triple.rizzlang", + "begin": "\"\"\"", + "end": "\"\"\"", + "patterns": [ + { + "name": "constant.character.escape.rizzlang", + "match": "\\\\." + } + ] + } + ] + }, + "comments": { + "patterns": [ + { + "name": "comment.line.number-sign.rizzlang", + "match": "#.*$" + } + ] + }, + "numbers": { + "patterns": [ + { + "name": "constant.numeric.rizzlang", + "match": "\\b[0-9]+(\\.[0-9]+)?\\b" + } + ] + }, + "function-call": { + "patterns": [ + { + "name": "entity.name.function.rizzlang", + "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(" + } + ] + }, + "decorator": { + "patterns": [ + { + "name": "entity.name.function.decorator.rizzlang", + "match": "^\\s*(lowkey|highkey|on god)\\s*$" + } + ] + } + }, + "scopeName": "source.rizzlang" +} \ No newline at end of file diff --git a/transpiler.py b/transpiler.py index 97e2d14..76818f2 100644 --- a/transpiler.py +++ b/transpiler.py @@ -9,6 +9,7 @@ import readline # For better REPL experience import py_compile import shutil from pathlib import Path +from vscode_extension_generator import generate_vscode_extension class Transpiler: def __init__(self, mapping_file): @@ -303,6 +304,9 @@ class Transpiler: def main(): parser = argparse.ArgumentParser(description='Transpile, execute, or compile custom language files') + # Common arguments that apply to all commands + parser.add_argument('-c', '--config', help='JSON mapping file (defaults to mapping.json in current directory)') + # Create subparsers for different commands subparsers = parser.add_subparsers(dest='command', help='Command to run') @@ -331,8 +335,9 @@ def main(): create_parser = subparsers.add_parser('create-mapping', help='Create a new mapping file with default values') create_parser.add_argument('output_file', help='Output file for the mapping') - # Common arguments - parser.add_argument('-c', '--config', help='JSON mapping file (defaults to mapping.json in current directory)') + # VS Code extension command + vscode_parser = subparsers.add_parser('vscode', help='Generate VS Code extension for syntax highlighting') + vscode_parser.add_argument('-o', '--output-dir', help='Output directory for the extension') args = parser.parse_args() @@ -376,6 +381,9 @@ def main(): elif args.command == 'repl': transpiler.start_repl() + + elif args.command == 'vscode': + generate_vscode_extension(args.config, args.output_dir) def create_default_mapping(filename): """Create a default mapping file with meme/slang terms.""" diff --git a/vscode_extension_generator.py b/vscode_extension_generator.py new file mode 100644 index 0000000..d481f64 --- /dev/null +++ b/vscode_extension_generator.py @@ -0,0 +1,275 @@ +import json +import os +import shutil +import argparse +import sys +from pathlib import Path + +def generate_vscode_extension(mapping_file, output_dir=None): + """Generate a VS Code extension for syntax highlighting based on a mapping file.""" + # Load the mapping file + with open(mapping_file, 'r') as f: + mapping_data = json.load(f) + + # Extract language info + if "language_info" in mapping_data: + language_info = mapping_data["language_info"] + language_name = language_info.get("name", "CustomLanguage") + language_extension = language_info.get("file_extension", ".custom") + language_description = language_info.get("description", "A custom programming language") + else: + language_name = "CustomLanguage" + language_extension = ".custom" + language_description = "A custom programming language" + + # Remove leading dot from extension if present + if language_extension.startswith('.'): + language_extension = language_extension[1:] + + # Create language ID (lowercase, no spaces) + language_id = language_name.lower().replace(' ', '-') + + # Determine output directory + if output_dir is None: + output_dir = f"vscode-{language_id}" + + # Create extension directory structure + os.makedirs(output_dir, exist_ok=True) + os.makedirs(os.path.join(output_dir, "syntaxes"), exist_ok=True) + + # Generate package.json + package_json = { + "name": f"{language_id}", + "displayName": language_name, + "description": language_description, + "version": "0.1.0", + "engines": { + "vscode": "^1.60.0" + }, + "categories": [ + "Programming Languages" + ], + "contributes": { + "languages": [ + { + "id": language_id, + "aliases": [language_name], + "extensions": [language_extension], + "configuration": f"./language-configuration.json" + } + ], + "grammars": [ + { + "language": language_id, + "scopeName": f"source.{language_id}", + "path": f"./syntaxes/{language_id}.tmLanguage.json" + } + ] + } + } + + # Generate language-configuration.json + language_config = { + "comments": { + "lineComment": "#", + }, + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + "autoClosingPairs": [ + { "open": "{", "close": "}" }, + { "open": "[", "close": "]" }, + { "open": "(", "close": ")" }, + { "open": "\"", "close": "\"", "notIn": ["string"] }, + { "open": "'", "close": "'", "notIn": ["string", "comment"] } + ], + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + "indentationRules": { + "increaseIndentPattern": "^.*:\\s*$", + "decreaseIndentPattern": "^\\s*$" + } + } + + # Extract keywords for syntax highlighting + keywords = mapping_data.get("keywords", {}) + special_patterns = mapping_data.get("special_patterns", {}) + + # Group keywords by their Python equivalents + keyword_groups = { + "control": [], # if, else, for, while, etc. + "declaration": [], # def, class, import, etc. + "operator": [], # and, or, not, in, etc. + "constant": [], # True, False, None + "builtin": [], # print, len, etc. + "storage": [], # global, nonlocal + "exception": [] # try, except, finally, raise + } + + # Map Python keywords to their groups + python_keyword_groups = { + "if": "control", "else": "control", "elif": "control", + "for": "control", "while": "control", "break": "control", + "continue": "control", "return": "control", "in": "operator", + "def": "declaration", "class": "declaration", "import": "declaration", + "from": "declaration", "as": "declaration", "with": "declaration", + "True": "constant", "False": "constant", "None": "constant", + "print": "builtin", "len": "builtin", "range": "builtin", + "global": "storage", "nonlocal": "storage", + "try": "exception", "except": "exception", "finally": "exception", + "raise": "exception", "assert": "exception", "Exception": "exception" + } + + # Categorize custom keywords + for custom_keyword, python_equiv in keywords.items(): + group = python_keyword_groups.get(python_equiv, "builtin") + keyword_groups[group].append(custom_keyword) + + # Add special pattern keywords + for pattern, python_equiv in special_patterns.items(): + # Extract the base keyword from the pattern (e.g., "no\\s+shot" -> "no shot") + base_keyword = pattern.replace("\\s+", " ").replace("\\(\\w+\\)", "").strip() + python_base = python_equiv.split()[0] if " " in python_equiv else python_equiv + group = python_keyword_groups.get(python_base, "builtin") + if base_keyword not in keyword_groups[group]: + keyword_groups[group].append(base_keyword) + + # Generate TextMate grammar + grammar = { + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": language_name, + "patterns": [ + { "include": "#keywords" }, + { "include": "#strings" }, + { "include": "#comments" }, + { "include": "#numbers" }, + { "include": "#function-call" }, + { "include": "#decorator" } + ], + "repository": { + "keywords": { + "patterns": [ + { + "name": "keyword.control." + language_id, + "match": "\\b(" + "|".join(keyword_groups["control"]) + ")\\b" + }, + { + "name": "keyword.declaration." + language_id, + "match": "\\b(" + "|".join(keyword_groups["declaration"]) + ")\\b" + }, + { + "name": "keyword.operator." + language_id, + "match": "\\b(" + "|".join(keyword_groups["operator"]) + ")\\b" + }, + { + "name": "constant.language." + language_id, + "match": "\\b(" + "|".join(keyword_groups["constant"]) + ")\\b" + }, + { + "name": "support.function." + language_id, + "match": "\\b(" + "|".join(keyword_groups["builtin"]) + ")\\b" + }, + { + "name": "storage.modifier." + language_id, + "match": "\\b(" + "|".join(keyword_groups["storage"]) + ")\\b" + }, + { + "name": "keyword.control.exception." + language_id, + "match": "\\b(" + "|".join(keyword_groups["exception"]) + ")\\b" + } + ] + }, + "strings": { + "patterns": [ + { + "name": "string.quoted.double." + language_id, + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape." + language_id, + "match": "\\\\." + } + ] + }, + { + "name": "string.quoted.single." + language_id, + "begin": "'", + "end": "'", + "patterns": [ + { + "name": "constant.character.escape." + language_id, + "match": "\\\\." + } + ] + }, + { + "name": "string.quoted.triple." + language_id, + "begin": "\"\"\"", + "end": "\"\"\"", + "patterns": [ + { + "name": "constant.character.escape." + language_id, + "match": "\\\\." + } + ] + } + ] + }, + "comments": { + "patterns": [ + { + "name": "comment.line.number-sign." + language_id, + "match": "#.*$" + } + ] + }, + "numbers": { + "patterns": [ + { + "name": "constant.numeric." + language_id, + "match": "\\b[0-9]+(\\.[0-9]+)?\\b" + } + ] + }, + "function-call": { + "patterns": [ + { + "name": "entity.name.function." + language_id, + "match": "\\b([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(" + } + ] + }, + "decorator": { + "patterns": [ + { + "name": "entity.name.function.decorator." + language_id, + "match": "^\\s*(" + "|".join(keyword_groups["storage"]) + ")\\s*$" + } + ] + } + }, + "scopeName": "source." + language_id + } + + # Write files + with open(os.path.join(output_dir, "package.json"), 'w') as f: + json.dump(package_json, f, indent=4) + + with open(os.path.join(output_dir, "language-configuration.json"), 'w') as f: + json.dump(language_config, f, indent=4) + + with open(os.path.join(output_dir, "syntaxes", f"{language_id}.tmLanguage.json"), 'w') as f: + json.dump(grammar, f, indent=4) + + # Create README.md + with open(os.path.join(output_dir, "README.md"), 'w') as f: + f.write(f"# {language_name} VS Code Extension\n\n") + f.write(f"This extension provides syntax highlighting for {language_name} files.\n") \ No newline at end of file