diff --git a/BarkBasic/BarkBasic.csproj b/BarkBasic/BarkBasic.csproj index 9fa8142..d301bc3 100644 --- a/BarkBasic/BarkBasic.csproj +++ b/BarkBasic/BarkBasic.csproj @@ -2,7 +2,7 @@ Exe - 0.1.120 + 0.1.200 alpha net8.0 enable diff --git a/BarkBasic/BasicInterpreter.cs b/BarkBasic/BasicInterpreter.cs new file mode 100644 index 0000000..f38a62a --- /dev/null +++ b/BarkBasic/BasicInterpreter.cs @@ -0,0 +1,45 @@ +namespace BarkBasic; + +public class BasicInterpreter +{ + private readonly Dictionary _commands = new(); + public void RegisterCommand(string name, Action> action) => _commands[name] = new Command(action); + + public void Run(string code) + { + var lines = code.Split('\n'); + foreach (var line in lines) + { + ExecuteLine(line); + } + } + + private void ExecuteLine(string line) + { + line = line.Trim(); + if (string.IsNullOrEmpty(line)) return; + + var parts = line.Split(' '); + var commandName = parts[0].ToUpper(); + var args = new List(parts); + args.RemoveAt(0); // Remove the command name from arguments + + if (_commands.ContainsKey(commandName)) + { + _commands[commandName].Execute(args); + } + else + { + Console.WriteLine($"Unknown command: {commandName}"); + } + } +} + +public class Command +{ + private readonly Action> _action; + + public Command(Action> action) => _action = action; + + public void Execute(List args) => _action.Invoke(args); +} \ No newline at end of file diff --git a/BarkBasic/ChangeLog.md b/BarkBasic/ChangeLog.md index b8cc90d..2f68f11 100644 --- a/BarkBasic/ChangeLog.md +++ b/BarkBasic/ChangeLog.md @@ -1,5 +1,9 @@ # Change Log +## 0.1.200 + +- Rewritten and simplified architecture. + ## 0.1.120 Moved the hard-coded example to a ``.basic`` file that is read from a Toml-based project file. diff --git a/BarkBasic/Lexer.cs b/BarkBasic/Lexer.cs deleted file mode 100644 index d215c20..0000000 --- a/BarkBasic/Lexer.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace BarkBasic; - -internal class Lexer(string input) -{ - static readonly Regex NumberRegex = new(@"\d+"); - static readonly Regex WhitespaceRegex = new(@"\s+"); - int _pos = 0; - - public Token GetNextToken() - { - while (true) - { - if (_pos >= input.Length) - return new Token(TokenType.EOF, null); - - if (char.IsDigit(input[_pos])) - { - var match = NumberRegex.Match(input, _pos); - _pos += match.Length; - return new Token(TokenType.Number, match.Value); - } - - if (!char.IsWhiteSpace(input[_pos])) - return input[_pos++] switch - { - '+' => new Token(TokenType.Plus, "+"), - '-' => new Token(TokenType.Minus, "-"), - '*' => new Token(TokenType.Multiply, "*"), - '/' => new Token(TokenType.Divide, "/"), - '(' => new Token(TokenType.LParen, "("), - ')' => new Token(TokenType.RParen, ")"), - _ => throw new Exception("Error parsing input") - }; - - _pos += WhitespaceRegex.Match(input, _pos).Length; - } - } -} \ No newline at end of file diff --git a/BarkBasic/Parser.cs b/BarkBasic/Parser.cs deleted file mode 100644 index 526aeb1..0000000 --- a/BarkBasic/Parser.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace BarkBasic; - -internal class Parser -{ - readonly Lexer _lexer; - Token _currentToken; - - public Parser(Lexer lexer) - { - _lexer = lexer; - _currentToken = _lexer.GetNextToken(); - } - - void Eat(TokenType tokenType) - { - if (_currentToken.Type == tokenType) - _currentToken = _lexer.GetNextToken(); - else - throw new Exception("Error parsing input"); - } - - public int Parse() - { - var result = Term(); - - while (_currentToken.Type is TokenType.Plus or TokenType.Minus) - { - switch (_currentToken.Type) - { - case TokenType.Plus: - Eat(TokenType.Plus); - result += Term(); - break; - case TokenType.Minus: - Eat(TokenType.Minus); - result -= Term(); - break; - case TokenType.Print: - Eat(TokenType.Print); - break; - } - } - - return result; - } - - int Term() - { - var result = Factor(); - - while (_currentToken.Type is TokenType.Multiply or TokenType.Divide) - { - switch (_currentToken.Type) - { - case TokenType.Multiply: - Eat(TokenType.Multiply); - result *= Factor(); - break; - case TokenType.Divide: - Eat(TokenType.Divide); - result /= Factor(); - break; - } - } - - return result; - } - - int Factor() - { - var token = _currentToken; - - switch (token.Type) - { - case TokenType.Number: - Eat(TokenType.Number); - return int.Parse(token.Value ?? throw new InvalidOperationException()); - case TokenType.LParen: - { - Eat(TokenType.LParen); - var result = Parse(); - Eat(TokenType.RParen); - return result; - } - case TokenType.Plus: - case TokenType.Minus: - case TokenType.Multiply: - case TokenType.Divide: - case TokenType.RParen: - case TokenType.Print: - case TokenType.EOF: - default: - throw new Exception("Error parsing input"); - } - } -} \ No newline at end of file diff --git a/BarkBasic/Program.cs b/BarkBasic/Program.cs index b723cac..7eda0ad 100644 --- a/BarkBasic/Program.cs +++ b/BarkBasic/Program.cs @@ -1,55 +1,37 @@ using BarkBasic; -using Tomlyn; -var projModel = File.ReadAllText(Path.Combine(Environment.CurrentDirectory, "Project.toml")); -var projFile = Toml.ToModel(projModel); -var code = ""; - -try +class Program { - var path = Path.Combine(Environment.CurrentDirectory, projFile.Code); - if (File.Exists(path)) - code = File.ReadAllText(path); -} -catch (Exception err) -{ - throw new FileNotFoundException(err.StackTrace); -} - -var lines = code.Split(Environment.NewLine); -var lineNumberRegex = new Regex(@"^\d+"); -var commentRegex = new Regex(@"REM.*$"); -var printRegex = new Regex(@"PRINT\s+'(.*)'"); -var gotoRegex = new Regex(@"GOTO\s+(\d+)"); - -foreach (var line in lines) -{ - if (string.IsNullOrWhiteSpace(line)) continue; - - var lineNumberMatch = lineNumberRegex.Match(line); - if (!lineNumberMatch.Success) throw new Exception($"Invalid line number in line: {line}"); - - var commentMatch = commentRegex.Match(line); - if (commentMatch.Success) continue; // Ignore comments - - var printMatch = printRegex.Match(line); - if (printMatch.Success) + static void Main() { - Console.WriteLine(printMatch.Groups[1].Value); - continue; - } - - // TODO: Finish goto function. - var gotoMatch = gotoRegex.Match(line); - if (gotoMatch.Success) - { - /*using var reader = new StreamReader(projFile.Code); + var interpreter = new BasicInterpreter(); + interpreter.RegisterCommand("PRINT", PrintCommand); + interpreter.RegisterCommand("LET", LetCommand); + // Add more commands as needed - for (var i = 1; i < int.Parse(gotoMatch.Groups[1].Value); i++) - reader.ReadLine(); - - Console.WriteLine(reader.ReadLine());*/ + string basicCode = @" + PRINT ""Hello, World!"" + LET A = 10 + PRINT A + "; + + interpreter.Run(basicCode); } - throw new Exception($"Unknown statement in line: {line}"); -} + static void PrintCommand(List args) + { + Console.WriteLine(string.Join(" ", args)); + } + + static void LetCommand(List args) + { + if (args.Count != 2 || !int.TryParse(args[1], out int value)) + { + Console.WriteLine("LET command syntax error"); + return; + } + + // Store the variable and its value in a dictionary or context + Console.WriteLine($"Variable {args[0]} set to {value}"); + } +} \ No newline at end of file diff --git a/BarkBasic/Token.cs b/BarkBasic/Token.cs deleted file mode 100644 index 540aef7..0000000 --- a/BarkBasic/Token.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace BarkBasic; - -internal record Token(TokenType Type, string? Value); \ No newline at end of file diff --git a/BarkBasic/TokenType.cs b/BarkBasic/TokenType.cs deleted file mode 100644 index 00a9912..0000000 --- a/BarkBasic/TokenType.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace BarkBasic; - -internal enum TokenType { Number, Plus, Minus, Multiply, Divide, LParen, RParen, Print, EOF } \ No newline at end of file diff --git a/README.md b/README.md index 506608f..a9a6a06 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ Bark Basic (working title) is a BASIC programming language paser. Right now, it ## Background -I've always wanted to write a parser for a programming language, but I had no idea where to start because there are so many ways to write one. Despite writing varies ones in the past, I've never really felt satisfied. In the past, I had the free version of ChatGPT write me drafts for a few starting points, but those were as buggy as you would expect. So, I decided to give it another whirl using Bing Copilot instead. After a few attempts, I asked it to use my algorithm from [CST.NET](https://github.com/tonytins/cstdotnet) as a reference, and it worked! +I've always wanted to write a parser for a programming language, but I had no idea where to start because there are so many ways to write one. Despite writing varies ones in the past, I've never really felt satisfied, so I asked AI. + +Originally, I used ChatGPT and I create a BASIC parser so I could use a template. While it worked, it was a little over-engineered for what I needed. Fast-forward to Deepseek making a splash, I tweaked the system prompt for the Coder version to be as good as NASA and requested the same thing but have it be expandable. Worked on the first try from [.NET Fiddle](https://dotnetfiddle.net/vPPVHD). ## License