From 6cc89cb69aab0bb384f001dbe25b1af6e28cb4f7 Mon Sep 17 00:00:00 2001 From: Ed94 Date: Wed, 6 Jul 2022 14:16:06 -0400 Subject: [PATCH] BAPFS - Lecture 3 --- .vscode/settings.json | 3 + Editor/Lecture.3..gd | 266 ++++++++++++++++++++++++++++++++++++++++++ Editor/Lecture.3.tscn | 8 ++ Editor/project.godot | 2 +- 4 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 .vscode/settings.json create mode 100644 Editor/Lecture.3..gd create mode 100644 Editor/Lecture.3.tscn diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b3667e2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.formatOnType": true +} \ No newline at end of file diff --git a/Editor/Lecture.3..gd b/Editor/Lecture.3..gd new file mode 100644 index 0000000..ba2b549 --- /dev/null +++ b/Editor/Lecture.3..gd @@ -0,0 +1,266 @@ +extends Node + +const JsonBeautifier = preload("res://ThirdParty/json_beautifier.gd") + +# This closesly follows the source provided in the lectures. +# Later on after the lectures are complete or when I deem +# Necessary there will be heavy refactors. + +const TokenType = \ +{ + Program = "Program", + + # Comments + CommentLine = "CommentLine", + CommentMultiLine = "CommentMultiLine", + + # Formatting + Whitespace = "Whitespace", + + # Literals + Number = "Number", + String = "String" +} + +const TokenSpec = \ +{ + TokenType.CommentLine : "^\/\/.*", + TokenType.CommentMultiLine : "^\/\\*[\\s\\S]*?\\*\/", + TokenType.Whitespace : "^\\s+", + TokenType.Number : "\\d+", + TokenType.String : "^\"[^\"]*\"" +} + +class Token: + var Type : String + var Value : String + + func toDict(): + var result = \ + { + Type = self.Type, + Value = self.Value + } + return result + +class Tokenizer: + var SrcTxt : String + var Cursor : int; + + # Sets up the tokenizer with the program source text. + func init(programSrcText): + SrcTxt = programSrcText + Cursor = 0 + + # Provides the next token in the source text. + func next_Token(): + if self.reached_EndOfTxt() == true : + return null + + var srcLeft = self.SrcTxt.substr(Cursor) + var regex = RegEx.new() + var token = Token.new() + + for type in TokenSpec : + regex.compile(TokenSpec[type]) + + var result = regex.search(srcLeft) + if result == null : + continue + + # Skip Comments + if type == TokenType.CommentLine || type == TokenType.CommentMultiLine : + self.Cursor += result.get_string().length() + return next_Token() + + # Skip Whitespace + if type == TokenType.Whitespace : + var addVal = result.get_string().length() + self.Cursor += addVal + + return next_Token() + + token.Type = type + token.Value = result.get_string() + self.Cursor += ( result.get_string().length() -1 ) + + return token + + var assertStrTmplt = "next_token: Source text not understood by tokenizer at Cursor pos: {value}" + var assertStr = assertStrTmplt.format({"value" : self.Cursor}) + assert(true != true, assertStr) + return null + + func reached_EndOfTxt(): + return self.Cursor >= ( self.SrcTxt.length() - 1 ) + +var GTokenizer = Tokenizer.new() + + +class SyntaxNode: + var Type : String + var Value # Not specifing a type implicity declares a Variant type. + + func toDict(): + var result = \ + { + Type = self.Type, + Value = self.Value + } + return result + +class ProgramNode: + var Type : String + var Body : Object + + func toDict(): + var result = \ + { + Type = self.Type, + Body = self.Body.toDict() + } + return result + +class Parser: + var TokenizerRef : Tokenizer + var NextToken : Token + + func eat(tokenType): + var currToken = self.NextToken + + assert(currToken != null, "eat: NextToken was null") + + var assertStrTmplt = "eat: Unexpected token: {value}, expected: {type}" + var assertStr = assertStrTmplt.format({"value" : currToken.Value, "type" : tokenType}) + + assert(currToken.Type == tokenType, assertStr) + + self.NextToken = self.TokenizerRef.next_Token() + + return currToken + + # Literal + # : NumericLiteral + # : StringLiteral + # ; + func parse_Literal(): + match self.NextToken.Type : + TokenType.Number: + return parse_NumericLiteral() + TokenType.String: + return parse_StringLiteral() + + assert(false, "parse_Literal: Was not able to detect valid literal type from NextToken") + + # NumericLiteral + # : Number + # ; + # + func parse_NumericLiteral(): + var Token = self.eat(TokenType.Number) + var \ + node = SyntaxNode.new() + node.Type = TokenType.Number + node.Value = int( Token.Value ) + + return node + + # StringLiteral + # : String + # ; + # + func parse_StringLiteral(): + var Token = self.eat(TokenType.String) + var \ + node = SyntaxNode.new() + node.Type = TokenType.String + node.Value = Token.Value.substr( 1, Token.Value.length() - 2 ) + + return node + + # Program + # : Literal + # ; + # + func parse_Program(): + var \ + node = ProgramNode.new() + node.Type = TokenType.Program + node.Body = parse_Literal() + + return node + + # Parses the text program description into an AST. + func parse(TokenizerRef): + self.TokenizerRef = TokenizerRef + + NextToken = TokenizerRef.next_Token() + + return parse_Program() + +var GParser = Parser.new() + + +var ProgramDescription : String + +func test(): + GTokenizer.init(ProgramDescription) + + var ast = GParser.parse(GTokenizer) + + print(JsonBeautifier.beautify_json(to_json(ast.toDict()))) + + +# Main Entry point. +func _ready(): + # Numerical test + ProgramDescription = "47" + test() + + # String Test + ProgramDescription = "\"hello\"" + test() + + # Whitespace test + ProgramDescription = " \"we got past whitespace\" " + test() + + # Comment Single Test + ProgramDescription = \ + """ + // Testing a comment + \"hello sir\" + """ + test() + + # Comment Multi-Line Test + ProgramDescription = \ + """ + /** + * + * Testing a comment + */ + \"may I have some grapes\" + """ + test() + + # Multi-statement test + ProgramDescription = \ + """ + // Testing a comment + \"hello sir\"; + + /** + * + * Testing a comment + */ + \"may I have some grapes\"; + """ + test() + +# Called every frame. 'delta' is the elapsed time since the previous frame. +#func _process(delta): +# pass + + + diff --git a/Editor/Lecture.3.tscn b/Editor/Lecture.3.tscn new file mode 100644 index 0000000..ae2465e --- /dev/null +++ b/Editor/Lecture.3.tscn @@ -0,0 +1,8 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://Lecture.3..gd" type="Script" id=1] + +[node name="Control" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +script = ExtResource( 1 ) diff --git a/Editor/project.godot b/Editor/project.godot index 02efe34..4b785e0 100644 --- a/Editor/project.godot +++ b/Editor/project.godot @@ -21,7 +21,7 @@ _global_script_class_icons={ [application] config/name="Parser" -run/main_scene="res://Lecture.2.tscn" +run/main_scene="res://Lecture.3.tscn" boot_splash/image="res://Branding/RDP_Class_cover_small.png" config/icon="res://Branding/RDP_Class_cover_small.png"