LangStudies/Editor/Lectures/Lecture.2.gd

211 lines
4.0 KiB
GDScript

extends Node
# 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.
enum TokenTypes \
{
Token_Number,
Token_String
}
const StrTokenTypes = \
{
Token_Number = "Number",
Token_String = "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 token = self.SrcTxt.substr(Cursor)
# Numbers
if token[self.Cursor].is_valid_integer() :
var \
numberTok = Token.new()
numberTok.Type = "Number"
numberTok.Value = ""
while token.length() > self.Cursor && token[self.Cursor].is_valid_integer() :
numberTok.Value += token[self.Cursor]
self.Cursor += 1
return numberTok
# String:
if token[self.Cursor] == '"' :
var \
stringTok = Token.new()
stringTok.Type = "String"
stringTok.Value = "\""
self.Cursor += 1
while token.length() > self.Cursor :
stringTok.Value += token[self.Cursor]
self.Cursor += 1
return stringTok
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 :
"Number":
return parse_NumericLiteral()
"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("Number")
var \
node = SyntaxNode.new()
node.Type = "NumericLiteral"
node.Value = int( Token.Value )
return node
# StringLiteral
# : String
# ;
#
func parse_StringLiteral():
var Token = self.eat("String")
var \
node = SyntaxNode.new()
node.Type = "StringLiteral"
node.Value = Token.Value.substr( 1, Token.Value.length() - 2 )
return node
# Program
# : Literal
# ;
#
func parse_Program():
var \
node = ProgramNode.new()
node.Type = "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()
# Main Entry point.
func _ready():
# Numerical test
var ProgramDescription = "47"
GTokenizer.init(ProgramDescription)
var ast = GParser.parse(GTokenizer)
print(JSON.print(ast.toDict(), "\t"))
# String Test
ProgramDescription = "\"hello\""
GTokenizer.init(ProgramDescription)
ast = GParser.parse(GTokenizer)
print(JSON.print(ast.toDict(), "\t"))
# Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta):
# pass