diff --git a/languages/elysium-lang - alex/README.md b/languages/elysium-lang - alex/README.md new file mode 100644 index 0000000..09c4c13 --- /dev/null +++ b/languages/elysium-lang - alex/README.md @@ -0,0 +1,103 @@ +# Elysium Language + +Welcome to **Elysium Language**, a programming language inspired by the elegance and simplicity of Python, the structure of JavaScript, and the functional aspects of Lisp. Elysium is designed for those who appreciate readability, maintainability, and the joy of writing clean code. + +## Inspiration + +The idea for Elysium Language came to me while working on various projects that required different programming paradigms. I found myself often wishing for a language that combined the best features of my favorite languages: + +- **Python's readability and simplicity**: Python's clean syntax and readability have always made coding a pleasant experience for me. I wanted Elysium to reflect that simplicity. +- **JavaScript's versatility**: JavaScript's ability to handle both object-oriented and functional programming inspired the versatile nature of Elysium. +- **Lisp's powerful macro system**: The functional programming capabilities and the macro system of Lisp provided a powerful paradigm that I wanted to incorporate into Elysium. + +## Features + +- **Clean and Readable Syntax**: Elysium emphasizes code readability and simplicity, making it easy to learn and use. +- **First-Class Functions**: Functions in Elysium are first-class citizens, allowing you to pass them around and use them as arguments. +- **Versatile Paradigms**: Whether you prefer object-oriented programming, functional programming, or a mix of both, Elysium has you covered. +- **Dynamic Typing**: Similar to Python and JavaScript, Elysium uses dynamic typing, allowing you to write flexible and adaptable code. +- **Comprehensive Standard Library**: Elysium comes with a rich standard library that provides essential tools and utilities for everyday programming tasks. + +## Installation + +To get started with Elysium, you'll need to clone the repository and install the necessary dependencies. Make sure you have Node.js installed on your machine. + +1. **Clone the repository**: + ```sh + git clone https://github.com/yourusername/elysium-lang.git + cd elysium-lang + ``` + +2. **Install dependencies**: + ```sh + npm install + ``` + +3. **Run your first Elysium program**: + ```sh + node src/index.js + ``` + +## Example Code + +Here's a simple Elysium program to give you a taste of what the language looks like: + +``` +let x = 10 +let y = 20 + +if x < y { + print x; +} else { + print y; +} + +while x > 0 { + print x; + x = x - 1; +} +``` + +## Language Syntax +# Variables +Variables are declared using the let keyword: + +``` +let x = 10 +let y = x + 1 +``` + +# Functions +# Note: this is a work in progress feature and is super buggy at the moment!!!! +Functions are defined using the function keyword: + +``` +function add(a, b) { + return a + b; +} +``` + +# Control Structures +Elysium supports standard control structures such as if, else, and while: + +``` +if condition { + // code block +} else { + // code block +} + +while x > 5 { + // code block +} +``` + +# Contributing +I welcome contributions from anyone who is passionate about making Elysium better. Whether it's bug fixes, new features, or documentation improvements, your help is appreciated. Please fork the repository, make your changes, and submit a pull request. + +# Contact +If you have any questions, suggestions, or feedback, feel free to reach out to me at alexanderli@hotmail.ca. You can also contact me on slack @alexanderli for updates on Elysium and other projects. + +Thank you for your interest in Elysium Language. Happy coding! + +Elysium Language is a personal project made with ❤️ by Alex. diff --git a/languages/elysium-lang - alex/package-lock.json b/languages/elysium-lang - alex/package-lock.json new file mode 100644 index 0000000..4032a42 --- /dev/null +++ b/languages/elysium-lang - alex/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "elysium-lang", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "elysium-lang", + "version": "1.0.0", + "license": "ISC" + } + } +} diff --git a/languages/elysium-lang - alex/package.json b/languages/elysium-lang - alex/package.json new file mode 100644 index 0000000..cba6ea8 --- /dev/null +++ b/languages/elysium-lang - alex/package.json @@ -0,0 +1,15 @@ +{ + "name": "elysium-lang", + "version": "1.0.0", + "description": "A simple programming language", + "main": "src/index.js", + "scripts": { + "start": "node src/index.js" + }, + "author": "Alex", + "license": "ISC", + "directories": { + "test": "test" + }, + "keywords": [] +} diff --git a/languages/elysium-lang - alex/src/index.js b/languages/elysium-lang - alex/src/index.js new file mode 100644 index 0000000..7edf087 --- /dev/null +++ b/languages/elysium-lang - alex/src/index.js @@ -0,0 +1,26 @@ +const fs = require('fs'); +const Lexer = require('./lexer'); +const Parser = require('./parser'); +const Interpreter = require('./interpreter'); + +// Read the input file +const input = fs.readFileSync('./test/test.ely', 'utf8'); + +// Tokenize the input +try { + const lexer = new Lexer(input); + const tokens = lexer.tokenize(); + console.log("Tokens:", tokens); + + // Parse the tokens + const parser = new Parser(tokens); + const ast = parser.parse(); + console.log("AST:", JSON.stringify(ast, null, 2)); + + // Interpret the AST + const interpreter = new Interpreter(); + interpreter.execute(ast); +} catch (error) { + console.error("Error:", error.message); + console.error(error.stack); +} diff --git a/languages/elysium-lang - alex/src/interpreter.js b/languages/elysium-lang - alex/src/interpreter.js new file mode 100644 index 0000000..a15b5dc --- /dev/null +++ b/languages/elysium-lang - alex/src/interpreter.js @@ -0,0 +1,104 @@ +class Interpreter { + constructor() { + this.environment = {}; + this.functions = {}; + } + + evaluate(node) { + switch (node.type) { + case 'Declaration': + this.environment[node.identifier] = this.evaluate(node.value); + break; + case 'Assignment': + if (!(node.identifier in this.environment)) { + throw new Error(`Undefined variable ${node.identifier}`); + } + this.environment[node.identifier] = this.evaluate(node.value); + break; + case 'BinaryExpression': + return this.evaluateBinaryExpression(node); + case 'Identifier': + if (!(node.name in this.environment)) { + throw new Error(`Undefined variable ${node.name}`); + } + return this.environment[node.name]; + case 'Literal': + return node.value; + case 'PrintStatement': + const value = this.evaluate(node.value); + console.log(value); + return value; + case 'WhileStatement': + return this.evaluateWhileStatement(node); + case 'IfStatement': + return this.evaluateIfStatement(node); + case 'FunctionDeclaration': + this.functions[node.name] = node; + break; + case 'ReturnStatement': + return this.evaluate(node.value); + default: + throw new Error(`Unknown node type: ${node.type}`); + } + } + + evaluateBinaryExpression(node) { + const left = this.evaluate(node.left); + const right = this.evaluate(node.right); + switch (node.operator) { + case '+': + return left + right; + case '-': + return left - right; + case '*': + return left * right; + case '/': + return left / right; + case '<': + return left < right; + case '>': + return left > right; + case '==': + return left == right; + case '<=': + return left <= right; + case '>=': + return left >= right; + case '!=': + return left != right; + default: + throw new Error(`Unknown operator: ${node.operator}`); + } + } + + evaluateWhileStatement(node) { + while (this.evaluate(node.condition)) { + for (const statement of node.body) { + this.evaluate(statement); + } + } + } + + evaluateIfStatement(node) { + if (this.evaluate(node.condition)) { + for (const statement of node.consequent) { + this.evaluate(statement); + } + } else if (node.alternate) { + for (const statement of node.alternate) { + this.evaluate(statement); + } + } + } + + execute(ast) { + ast.forEach(node => { + console.log("Executing node:", node); + this.evaluate(node); + }); + console.log("Final environment:", this.environment); + console.log("Values of variables:", this.environment); + } +} + +module.exports = Interpreter; diff --git a/languages/elysium-lang - alex/src/lexer.js b/languages/elysium-lang - alex/src/lexer.js new file mode 100644 index 0000000..6e6c95f --- /dev/null +++ b/languages/elysium-lang - alex/src/lexer.js @@ -0,0 +1,114 @@ +class Lexer { + constructor(input) { + this.input = input; + this.position = 0; + this.currentChar = this.input[this.position]; + } + + advance() { + this.position++; + if (this.position < this.input.length) { + this.currentChar = this.input[this.position]; + } else { + this.currentChar = null; + } + } + + tokenize() { + const tokens = []; + while (this.currentChar !== null) { + if (this.isWhitespace(this.currentChar)) { + this.advance(); + } else if (this.isLetter(this.currentChar)) { + tokens.push(this.identifier()); + } else if (this.isDigit(this.currentChar)) { + tokens.push(this.number()); + } else if (this.currentChar === '=' && this.peek() === '=') { + tokens.push(this.equalityOperator()); + } else if (this.currentChar === '!' && this.peek() === '=') { + tokens.push(this.equalityOperator()); + } else if (this.currentChar === '=' || this.currentChar === '<' || this.currentChar === '>' || this.currentChar === '!') { + tokens.push(this.relationalOperator()); + } else if (this.currentChar === '+') { + tokens.push({ type: 'OPERATOR', value: '+' }); + this.advance(); + } else if (this.currentChar === '-') { + tokens.push({ type: 'OPERATOR', value: '-' }); + this.advance(); + } else if (this.currentChar === '*') { + tokens.push({ type: 'OPERATOR', value: '*' }); + this.advance(); + } else if (this.currentChar === '/') { + tokens.push({ type: 'OPERATOR', value: '/' }); + this.advance(); + } else if (this.currentChar === '{' || this.currentChar === '}' || this.currentChar === '(' || this.currentChar === ')' || this.currentChar === ',') { + tokens.push({ type: 'PUNCTUATION', value: this.currentChar }); + this.advance(); + } else { + throw new Error(`Unexpected character: ${this.currentChar}`); + } + } + return tokens; + } + + peek() { + return this.position + 1 < this.input.length ? this.input[this.position + 1] : null; + } + + isWhitespace(char) { + return /\s/.test(char); + } + + isLetter(char) { + return /[a-zA-Z]/.test(char); + } + + isDigit(char) { + return /[0-9]/.test(char); + } + + identifier() { + let result = ''; + while (this.currentChar !== null && this.isLetter(this.currentChar)) { + result += this.currentChar; + this.advance(); + } + const type = this.isKeyword(result) ? 'KEYWORD' : 'IDENTIFIER'; + return { type, value: result }; + } + + isKeyword(word) { + return ['let', 'print', 'while', 'if', 'else', 'function', 'return'].includes(word); + } + + number() { + let result = ''; + while (this.currentChar !== null && this.isDigit(this.currentChar)) { + result += this.currentChar; + this.advance(); + } + return { type: 'NUMBER', value: Number(result) }; + } + + relationalOperator() { + let result = this.currentChar; + this.advance(); + if (this.currentChar === '=') { + result += this.currentChar; + this.advance(); + } + return { type: 'OPERATOR', value: result }; + } + + equalityOperator() { + let result = this.currentChar; + this.advance(); + if (this.currentChar === '=') { + result += this.currentChar; + this.advance(); + } + return { type: 'OPERATOR', value: result }; + } +} + +module.exports = Lexer; diff --git a/languages/elysium-lang - alex/src/parser.js b/languages/elysium-lang - alex/src/parser.js new file mode 100644 index 0000000..2fd0bbc --- /dev/null +++ b/languages/elysium-lang - alex/src/parser.js @@ -0,0 +1,208 @@ +class Parser { + constructor(tokens) { + this.tokens = tokens; + this.position = 0; + } + + parse() { + console.log("Parsing tokens"); + const ast = []; + while (this.position < this.tokens.length) { + console.log("Current token position:", this.position); + ast.push(this.parseStatement()); + } + console.log("AST:", JSON.stringify(ast, null, 2)); + return ast; + } + + parseStatement() { + const token = this.tokens[this.position]; + console.log("Parsing statement:", token); + if (token.type === 'KEYWORD' && token.value === 'let') { + return this.parseDeclaration(); + } else if (token.type === 'IDENTIFIER') { + return this.parseAssignment(); + } else if (token.type === 'KEYWORD' && token.value === 'print') { + return this.parsePrintStatement(); + } else if (token.type === 'KEYWORD' && token.value === 'while') { + return this.parseWhileStatement(); + } else if (token.type === 'KEYWORD' && token.value === 'if') { + return this.parseIfStatement(); + } else if (token.type === 'KEYWORD' && token.value === 'function') { + return this.parseFunctionDeclaration(); + } + throw new Error(`Unexpected token type at position ${this.position}: ${token.type}`); + } + + parseDeclaration() { + console.log("Parsing declaration"); + this.position++; // Consume 'let' + const identifierToken = this.tokens[this.position++]; + if (identifierToken.type !== 'IDENTIFIER') { + throw new Error(`Expected identifier after 'let', but got ${identifierToken.type}`); + } + const identifier = identifierToken.value; + console.log("Parsed declaration identifier:", identifier); + + const equalToken = this.tokens[this.position++]; + if (equalToken.type !== 'OPERATOR' || equalToken.value !== '=') { + throw new Error("Expected '=' after identifier in declaration"); + } + + const value = this.parseExpression(); + console.log("Parsed declaration value:", value); + return { type: 'Declaration', identifier, value }; + } + + parseAssignment() { + console.log("Parsing assignment"); + const identifier = this.tokens[this.position++].value; + console.log("Parsed identifier:", identifier); + + const equalToken = this.tokens[this.position++]; + if (equalToken.type !== 'OPERATOR' || equalToken.value !== '=') { + throw new Error("Expected '=' after identifier"); + } + + const value = this.parseExpression(); + console.log("Parsed assignment value:", value); + return { type: 'Assignment', identifier, value }; + } + + parsePrintStatement() { + console.log("Parsing print statement"); + this.position++; // Consume 'print' + const value = this.parseExpression(); + console.log("Parsed print value:", value); + return { type: 'PrintStatement', value }; + } + + parseWhileStatement() { + console.log("Parsing while statement"); + this.position++; // Consume 'while' + const condition = this.parseExpression(); + this.expectPunctuation('{'); + const body = []; + while (this.tokens[this.position].value !== '}') { + body.push(this.parseStatement()); + } + this.expectPunctuation('}'); + console.log("Parsed while statement condition:", condition); + console.log("Parsed while statement body:", body); + return { type: 'WhileStatement', condition, body }; + } + + parseIfStatement() { + console.log("Parsing if statement"); + this.position++; // Consume 'if' + const condition = this.parseExpression(); + this.expectPunctuation('{'); + const consequent = []; + while (this.tokens[this.position].value !== '}') { + consequent.push(this.parseStatement()); + } + this.expectPunctuation('}'); + let alternate = null; + if (this.tokens[this.position] && this.tokens[this.position].value === 'else') { + alternate = this.parseElseStatement(); + } + console.log("Parsed if statement condition:", condition); + console.log("Parsed if statement consequent:", consequent); + console.log("Parsed if statement alternate:", alternate); + return { type: 'IfStatement', condition, consequent, alternate }; + } + + parseElseStatement() { + console.log("Parsing else statement"); + this.position++; // Consume 'else' + this.expectPunctuation('{'); + const alternate = []; + while (this.tokens[this.position].value !== '}') { + alternate.push(this.parseStatement()); + } + this.expectPunctuation('}'); + console.log("Parsed else statement body:", alternate); + return alternate; + } + + parseFunctionDeclaration() { + console.log("Parsing function declaration"); + this.position++; // Consume 'function' + const nameToken = this.tokens[this.position++]; + if (nameToken.type !== 'IDENTIFIER') { + throw new Error('Expected function name'); + } + const name = nameToken.value; + console.log("Parsed function name:", name); + this.expectPunctuation('('); + const parameters = []; + while (this.tokens[this.position].type !== 'PUNCTUATION' || this.tokens[this.position].value !== ')') { + const paramToken = this.tokens[this.position++]; + if (paramToken.type !== 'IDENTIFIER') { + throw new Error('Expected parameter name'); + } + parameters.push(paramToken.value); + if (this.tokens[this.position].type === 'PUNCTUATION' && this.tokens[this.position].value === ',') { + this.position++; + } + } + this.expectPunctuation(')'); + this.expectPunctuation('{'); + const body = []; + while (this.tokens[this.position].type !== 'PUNCTUATION' || this.tokens[this.position].value !== '}') { + body.push(this.parseStatement()); + } + this.expectPunctuation('}'); + console.log("Parsed function parameters:", parameters); + console.log("Parsed function body:", body); + return { type: 'FunctionDeclaration', name, parameters, body }; + } + + expectPunctuation(char) { + const token = this.tokens[this.position++]; + if (token.type !== 'PUNCTUATION' || token.value !== char) { + throw new Error(`Expected punctuation: '${char}', but got '${token.value}'`); + } + } + + parseExpression() { + let left = this.parseTerm(); + while (this.tokens[this.position] && this.tokens[this.position].type === 'OPERATOR' && ['+', '-', '<', '>', '<=', '>=', '==', '!='].includes(this.tokens[this.position].value)) { + const operator = this.tokens[this.position].value; + this.position++; + const right = this.parseTerm(); + left = { type: 'BinaryExpression', left, operator, right }; + console.log("Parsed binary expression:", left); + } + return left; + } + + parseTerm() { + let left = this.parseFactor(); + while (this.tokens[this.position] && this.tokens[this.position].type === 'OPERATOR' && ['*', '/'].includes(this.tokens[this.position].value)) { + const operator = this.tokens[this.position].value; + this.position++; + const right = this.parseFactor(); + left = { type: 'BinaryExpression', left, operator, right }; + console.log("Parsed binary expression:", left); + } + return left; + } + + parseFactor() { + const token = this.tokens[this.position++]; + console.log("Parsing factor:", token); + if (token.type === 'NUMBER') { + return { type: 'Literal', value: token.value }; + } else if (token.type === 'IDENTIFIER') { + return { type: 'Identifier', name: token.value }; + } else if (token.type === 'PUNCTUATION' && token.value === '(') { + const expression = this.parseExpression(); + this.expectPunctuation(')'); + return expression; + } + throw new Error(`Unexpected token type at position ${this.position - 1}: ${token.type}`); + } +} + +module.exports = Parser; diff --git a/languages/elysium-lang - alex/test/test.ely b/languages/elysium-lang - alex/test/test.ely new file mode 100644 index 0000000..47837b4 --- /dev/null +++ b/languages/elysium-lang - alex/test/test.ely @@ -0,0 +1,16 @@ +let x = 1 +let y = x + 1 + + +if y < 1 { + print y +} else { + print x +} + +while x < 6 { + print x + x = x + 1 +} + +