mirror of
https://github.com/Ed94/LangStudies.git
synced 2024-11-10 12:24:53 -08:00
1259 lines
28 KiB
GDScript3
1259 lines
28 KiB
GDScript3
|
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.
|
||
|
|
||
|
const TokenType = \
|
||
|
{
|
||
|
Program = "Program",
|
||
|
|
||
|
# Comments
|
||
|
CommentLine = "CommentLine",
|
||
|
CommentMultiLine = "CommentMultiLine",
|
||
|
|
||
|
# Formatting
|
||
|
Whitespace = "Whitespace",
|
||
|
|
||
|
# Expressions
|
||
|
ExpressionPStart = "ExpressionParenthesisStart",
|
||
|
ExpressionPEnd = "ExpressionParenthesisEnd",
|
||
|
expr_SqBStart = "ExpressionSquareBracketStart",
|
||
|
expr_SqBEnd = "ExpressionSquareBracketEnd",
|
||
|
|
||
|
# Logical
|
||
|
RelationalOp = "RelationalOperator",
|
||
|
EqualityOp = "EqualityOperator",
|
||
|
Logical_And = "Logical_And_Op",
|
||
|
Logical_Or = "Logical_Or_Op",
|
||
|
Logical_Not = "Logical_Not_Op",
|
||
|
|
||
|
# Arithmetic
|
||
|
ComplexAssignment = "ComplexAssignment",
|
||
|
Assignment = "Assignment",
|
||
|
AdditiveOp = "AdditiveOperator",
|
||
|
MultiplicativeOp = "MultiplicativeOperator",
|
||
|
|
||
|
# Conditional
|
||
|
Conditional_if = "if Conditional",
|
||
|
Conditional_else = "else Conditional",
|
||
|
|
||
|
# Statements
|
||
|
StatementEnd = "StatementEnd",
|
||
|
StmtBlockStart = "BlockStatementStart",
|
||
|
StmtBlockEnd = "BlockStatementEnd",
|
||
|
CommaDelimiter = "CommaDelimiter",
|
||
|
MemberDelimiter = "MemberDelimiter",
|
||
|
|
||
|
# Literals
|
||
|
Number = "Number",
|
||
|
String = "String",
|
||
|
|
||
|
# Symbols
|
||
|
Bool_true = "Boolean True",
|
||
|
Bool_false = "Boolean False",
|
||
|
VarDeclare = "Variable Declaration",
|
||
|
Identifier = "Identifier",
|
||
|
NullValue = "Null Value",
|
||
|
While = "While",
|
||
|
Do = "Do",
|
||
|
For = "For",
|
||
|
FuncDeclare = "Function Delcaration",
|
||
|
Return = "Return"
|
||
|
}
|
||
|
|
||
|
const TokenSpec = \
|
||
|
{
|
||
|
# Comments
|
||
|
TokenType.CommentLine : "^\\/\\/.*",
|
||
|
TokenType.CommentMultiLine : "^\\/\\*[\\s\\S]*?\\*\\/",
|
||
|
|
||
|
# Formatting
|
||
|
TokenType.Whitespace : "^\\s+",
|
||
|
|
||
|
# Expressions
|
||
|
TokenType.ExpressionPStart : "^\\(",
|
||
|
TokenType.ExpressionPEnd : "^\\)",
|
||
|
TokenType.expr_SqBStart : "^\\[",
|
||
|
TokenType.expr_SqBEnd : "^\\]",
|
||
|
|
||
|
# Logical
|
||
|
TokenType.RelationalOp : "^[>\\<]=?",
|
||
|
TokenType.EqualityOp : "^[=!]=",
|
||
|
TokenType.Logical_And : "^&&",
|
||
|
TokenType.Logical_Or : "^\\|\\|",
|
||
|
TokenType.Logical_Not : "^!",
|
||
|
|
||
|
# Arithmetic
|
||
|
TokenType.ComplexAssignment : "^[*\\/\\+\\-]=",
|
||
|
TokenType.Assignment : "^=",
|
||
|
TokenType.AdditiveOp : "^[+\\-]",
|
||
|
TokenType.MultiplicativeOp : "^[*\\/]",
|
||
|
|
||
|
# Literal
|
||
|
TokenType.Number : "\\d+",
|
||
|
TokenType.String : "^\"[^\"]*\"",
|
||
|
|
||
|
TokenType.Conditional_if : "^\\bif\\b",
|
||
|
TokenType.Conditional_else : "^\\belse\\b",
|
||
|
|
||
|
# Statements
|
||
|
TokenType.StatementEnd : "^;",
|
||
|
TokenType.StmtBlockStart : "^{",
|
||
|
TokenType.StmtBlockEnd : "^}",
|
||
|
TokenType.CommaDelimiter : "^,",
|
||
|
TokenType.MemberDelimiter : "^\\.",
|
||
|
|
||
|
# Symbols
|
||
|
TokenType.Bool_true : "^\\btrue\\b",
|
||
|
TokenType.Bool_false : "^\\bfalse\\b",
|
||
|
TokenType.VarDeclare : "^\\blet\\b",
|
||
|
TokenType.NullValue : "^\\bnull\\b",
|
||
|
TokenType.While : "^\\bwhile\\b",
|
||
|
TokenType.Do : "^\\bdo\\b",
|
||
|
TokenType.For : "^\\bfor\\b",
|
||
|
TokenType.FuncDeclare : "^\\bdef\\b",
|
||
|
TokenType.Return : "^\\breturn\\b",
|
||
|
TokenType.Identifier : "^\\w+"
|
||
|
}
|
||
|
|
||
|
class Token:
|
||
|
var Type : String
|
||
|
var Value : String
|
||
|
|
||
|
func to_Dictionary():
|
||
|
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 reached_EndOfTxt() == true :
|
||
|
return null
|
||
|
|
||
|
var srcLeft = 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 || result.get_start() != 0 :
|
||
|
continue
|
||
|
|
||
|
# Skip Comments
|
||
|
if type == TokenType.CommentLine || type == TokenType.CommentMultiLine :
|
||
|
Cursor += result.get_string().length()
|
||
|
return next_Token()
|
||
|
|
||
|
# Skip Whitespace
|
||
|
if type == TokenType.Whitespace :
|
||
|
var addVal = result.get_string().length()
|
||
|
Cursor += addVal
|
||
|
|
||
|
return next_Token()
|
||
|
|
||
|
token.Type = type
|
||
|
token.Value = result.get_string()
|
||
|
Cursor += ( result.get_string().length() )
|
||
|
|
||
|
return token
|
||
|
|
||
|
var assertStrTmplt = "next_token: Source text not understood by tokenizer at Cursor pos: {value}"
|
||
|
var assertStr = assertStrTmplt.format({"value" : Cursor})
|
||
|
assert(true != true, assertStr)
|
||
|
return null
|
||
|
|
||
|
func reached_EndOfTxt():
|
||
|
return Cursor >= ( SrcTxt.length() )
|
||
|
|
||
|
var GTokenizer = Tokenizer.new()
|
||
|
|
||
|
|
||
|
|
||
|
const AST_Format = \
|
||
|
{
|
||
|
Dictionary = "Dictionary",
|
||
|
SExpression = "S-Expression"
|
||
|
}
|
||
|
|
||
|
const SyntaxNodeType = \
|
||
|
{
|
||
|
NumericLiteral = "NumericLiteral",
|
||
|
StringLiteral = "StringLiteral",
|
||
|
ExpressionStatement = "ExpressionStatement",
|
||
|
BlockStatement = "BlockStatement",
|
||
|
EmptyStatement = "EmptyStatement",
|
||
|
BinaryExpression = "BinaryExpression",
|
||
|
Identifier = "Identifier",
|
||
|
AssignmentExpression = "AssignmentExpression",
|
||
|
VariableStatement = "VariableStatement",
|
||
|
VariableDeclaration = "VariableDeclaration",
|
||
|
ConditionalStatement = "ConditionalStatement",
|
||
|
BooleanLiteral = "BooleanLiteral",
|
||
|
NullLiteral = "NullLiteral",
|
||
|
LogicalExpression = "LogicalExpression",
|
||
|
UnaryExpression = "UnaryExpression",
|
||
|
WhileStatement = "WhileStatement",
|
||
|
DoWhileStatement = "DoWhileStatement",
|
||
|
ForStatement = "ForStatement",
|
||
|
FunctionDeclaration = "FunctionDeclaration",
|
||
|
ReturnStatement = "ReturnStatement",
|
||
|
MemberExpression = "MemberExpression",
|
||
|
CallExpression = "CallExpression"
|
||
|
}
|
||
|
|
||
|
class SyntaxNode:
|
||
|
var Type : String
|
||
|
var Value # Not specifing a type implicity declares a Variant type.
|
||
|
|
||
|
func array_Serialize(array, fn_objSerializer) :
|
||
|
var result = []
|
||
|
for entry in array :
|
||
|
if typeof(entry) == TYPE_ARRAY :
|
||
|
result.append( array_Serialize( entry, fn_objSerializer ))
|
||
|
elif typeof(entry) == TYPE_OBJECT :
|
||
|
fn_objSerializer.set_instance(entry)
|
||
|
result.append( fn_objSerializer.call_func() )
|
||
|
else :
|
||
|
result.append( entry )
|
||
|
|
||
|
return result
|
||
|
|
||
|
func to_SExpression():
|
||
|
var expression = [ Type ]
|
||
|
|
||
|
if typeof(Value) == TYPE_ARRAY :
|
||
|
var \
|
||
|
to_SExpression_Fn = FuncRef.new()
|
||
|
to_SExpression_Fn.set_function("to_SExpression")
|
||
|
|
||
|
var array = array_Serialize( self.Value, to_SExpression_Fn )
|
||
|
|
||
|
expression.append(array)
|
||
|
return expression
|
||
|
|
||
|
if typeof(Value) == TYPE_OBJECT :
|
||
|
var result = [ Type, Value.to_SExpression() ]
|
||
|
return result
|
||
|
|
||
|
expression.append(Value)
|
||
|
return expression
|
||
|
|
||
|
func to_Dictionary():
|
||
|
if typeof(Value) == TYPE_ARRAY :
|
||
|
var \
|
||
|
to_Dictionary_Fn = FuncRef.new()
|
||
|
to_Dictionary_Fn.set_function("to_Dictionary")
|
||
|
|
||
|
var array = array_Serialize( self.Value, to_Dictionary_Fn )
|
||
|
var result = \
|
||
|
{
|
||
|
Type = self.Type,
|
||
|
Value = array
|
||
|
}
|
||
|
return result
|
||
|
|
||
|
if typeof(Value) == TYPE_OBJECT :
|
||
|
var result = \
|
||
|
{
|
||
|
Type = self.Type,
|
||
|
Value = self.Value.to_Dictionary()
|
||
|
}
|
||
|
return result
|
||
|
|
||
|
var result = \
|
||
|
{
|
||
|
Type = self.Type,
|
||
|
Value = self.Value
|
||
|
}
|
||
|
return result
|
||
|
|
||
|
class Parser:
|
||
|
var TokenizerRef : Tokenizer
|
||
|
var NextToken : Token
|
||
|
|
||
|
# --------------------------------------------------------------------- HELPERS
|
||
|
|
||
|
# Gets the next token only if the current token is the specified intended token (tokenType)
|
||
|
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)
|
||
|
|
||
|
NextToken = TokenizerRef.next_Token()
|
||
|
|
||
|
return currToken
|
||
|
|
||
|
func is_Literal():
|
||
|
return \
|
||
|
NextToken.Type == TokenType.Number \
|
||
|
|| NextToken.Type == TokenType.String \
|
||
|
|| NextToken.Type == TokenType.Bool_true \
|
||
|
|| NextToken.Type == TokenType.Bool_false \
|
||
|
|| NextToken.Type == TokenType.NullValue
|
||
|
|
||
|
# BinaryExpression
|
||
|
# : MultiplicativeExpression
|
||
|
# | AdditiveExpression
|
||
|
# ;
|
||
|
func parse_BinaryExpression(parse_fn, operatorToken):
|
||
|
var left = parse_fn.call_func()
|
||
|
|
||
|
while NextToken.Type == operatorToken:
|
||
|
var operator = eat(operatorToken)
|
||
|
var right = parse_fn.call_func()
|
||
|
|
||
|
var \
|
||
|
nestedNode = SyntaxNode.new()
|
||
|
nestedNode.Type = SyntaxNodeType.BinaryExpression
|
||
|
nestedNode.Value = []
|
||
|
nestedNode.Value.append(operator.Value)
|
||
|
nestedNode.Value.append(left)
|
||
|
nestedNode.Value.append(right)
|
||
|
|
||
|
left = nestedNode;
|
||
|
|
||
|
return left
|
||
|
|
||
|
# LogicalExpression
|
||
|
# : LogicalAndExpression
|
||
|
# | LogicalOrExpression
|
||
|
# ;
|
||
|
func parse_LogicalExpression(parse_fn, operatorToken):
|
||
|
var left = parse_fn.call_func()
|
||
|
|
||
|
while NextToken.Type == operatorToken :
|
||
|
var operator = eat(operatorToken).Value
|
||
|
var right = parse_fn.call_func()
|
||
|
|
||
|
var \
|
||
|
nestedNode = SyntaxNode.new()
|
||
|
nestedNode.Type = SyntaxNodeType.LogicalExpression
|
||
|
nestedNode.Value = []
|
||
|
nestedNode.Value.append(operator)
|
||
|
nestedNode.Value.append(left)
|
||
|
nestedNode.Value.append(right)
|
||
|
|
||
|
left = nestedNode
|
||
|
|
||
|
return left
|
||
|
|
||
|
# ------------------------------------------------------------------ END HELPERS
|
||
|
|
||
|
# Parses the text program description into an AST.
|
||
|
func parse(TokenizerRef):
|
||
|
self.TokenizerRef = TokenizerRef
|
||
|
|
||
|
NextToken = TokenizerRef.next_Token()
|
||
|
|
||
|
return parse_Program()
|
||
|
|
||
|
# > parse
|
||
|
# Program
|
||
|
# : StatementList
|
||
|
# : Literal
|
||
|
# ;
|
||
|
func parse_Program():
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = TokenType.Program
|
||
|
node.Value = parse_StatementList(null)
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > Program
|
||
|
# > BlockStatement
|
||
|
# StatementList
|
||
|
# : Statement
|
||
|
# | StatementList Statement -> Statement ...
|
||
|
# ;
|
||
|
func parse_StatementList(endToken):
|
||
|
var statementList = [ parse_Statement() ]
|
||
|
|
||
|
while NextToken != null && NextToken.Type != endToken :
|
||
|
statementList.append( parse_Statement() )
|
||
|
|
||
|
return statementList
|
||
|
|
||
|
# > StatementList
|
||
|
# > If_Statement
|
||
|
# > WhileStatement
|
||
|
# Statement
|
||
|
# : ExpressionStatement
|
||
|
# | BlockStatement
|
||
|
# | EmptyStatement
|
||
|
# | VariableStatement
|
||
|
# | If_Statement
|
||
|
# | IterationStatement
|
||
|
# | FunctionDeclaration
|
||
|
# | ReturnStatement
|
||
|
# ;
|
||
|
func parse_Statement():
|
||
|
if NextToken == null :
|
||
|
return null
|
||
|
|
||
|
match NextToken.Type :
|
||
|
TokenType.Conditional_if :
|
||
|
return parse_If_Statement()
|
||
|
TokenType.StatementEnd :
|
||
|
return parse_EmptyStatement()
|
||
|
TokenType.StmtBlockStart :
|
||
|
return parse_BlockStatement()
|
||
|
TokenType.VarDeclare :
|
||
|
return parse_VariableStatement()
|
||
|
TokenType.While :
|
||
|
return parse_IterationStatement()
|
||
|
TokenType.Do :
|
||
|
return parse_IterationStatement()
|
||
|
TokenType.For :
|
||
|
return parse_IterationStatement()
|
||
|
TokenType.FuncDeclare:
|
||
|
return parse_FunctionDeclaration()
|
||
|
TokenType.Return:
|
||
|
return parse_ReturnStatement()
|
||
|
|
||
|
return parse_ExpressionStatement()
|
||
|
|
||
|
# If Statement
|
||
|
# : if ( Expression ) Statement
|
||
|
# | if ( Expression ) Statement else Statement
|
||
|
# ;
|
||
|
func parse_If_Statement():
|
||
|
eat(TokenType.Conditional_if)
|
||
|
|
||
|
eat(TokenType.ExpressionPStart)
|
||
|
var condition = parse_Expression()
|
||
|
eat(TokenType.ExpressionPEnd)
|
||
|
|
||
|
var consequent = parse_Statement()
|
||
|
var alternative = null
|
||
|
|
||
|
if NextToken != null && NextToken.Type == TokenType.Conditional_else :
|
||
|
eat(TokenType.Conditional_else)
|
||
|
alternative = parse_Statement()
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.ConditionalStatement
|
||
|
node.Value = [ condition, consequent, alternative ]
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > Statement
|
||
|
# EmptyStatement
|
||
|
# ;
|
||
|
func parse_EmptyStatement():
|
||
|
eat(TokenType.StatementEnd)
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.EmptyStatement
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > Statement
|
||
|
# BlockStatement
|
||
|
# : { OptStatementList }
|
||
|
# ;
|
||
|
func parse_BlockStatement():
|
||
|
eat(TokenType.StmtBlockStart)
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.BlockStatement
|
||
|
|
||
|
if NextToken.Type != TokenType.StmtBlockEnd :
|
||
|
node.Value = parse_StatementList(TokenType.StmtBlockEnd)
|
||
|
else :
|
||
|
node.Value = []
|
||
|
|
||
|
eat(TokenType.StmtBlockEnd)
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > Statement
|
||
|
# VariableStatement
|
||
|
# : VariableStatementInit StatementEnd
|
||
|
# ;
|
||
|
func parse_VariableStatement():
|
||
|
var varStatement = parse_VariableStatementInit()
|
||
|
|
||
|
eat(TokenType.StatementEnd)
|
||
|
|
||
|
return varStatement
|
||
|
|
||
|
# > VariableStatement
|
||
|
# VariableStatementInit
|
||
|
# : VarDeclare VariableDeclarationList
|
||
|
# ;
|
||
|
func parse_VariableStatementInit():
|
||
|
eat(TokenType.VarDeclare)
|
||
|
|
||
|
var declarations = parse_VariableDeclarationList()
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.VariableStatement
|
||
|
node.Value = declarations
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > Statement
|
||
|
# IterationStatement
|
||
|
# : WhileStatement
|
||
|
# | DoWhileStatement
|
||
|
# | ForStatement
|
||
|
# ;
|
||
|
func parse_IterationStatement():
|
||
|
match NextToken.Type:
|
||
|
TokenType.While :
|
||
|
return parse_WhileStatement()
|
||
|
TokenType.Do :
|
||
|
return parse_DoWhileStatement()
|
||
|
TokenType.For :
|
||
|
return parse_ForStatement()
|
||
|
|
||
|
# > IterationStatement
|
||
|
# WhileStatement
|
||
|
# : while ( Expression ) Statement
|
||
|
# ;
|
||
|
func parse_WhileStatement():
|
||
|
eat(TokenType.While)
|
||
|
|
||
|
eat(TokenType.ExpressionPStart)
|
||
|
var condition = parse_Expression()
|
||
|
eat(TokenType.ExpressionPEnd)
|
||
|
|
||
|
var body = parse_Statement()
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.WhileStatement
|
||
|
node.Value = [ condition, body ]
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > IterationStatement
|
||
|
# DoWhileStatement
|
||
|
# : do Statement while ( Expression )
|
||
|
# ;
|
||
|
func parse_DoWhileStatement():
|
||
|
eat(TokenType.Do)
|
||
|
|
||
|
var body = parse_Statement()
|
||
|
|
||
|
eat(TokenType.While)
|
||
|
|
||
|
eat(TokenType.ExpressionPStart)
|
||
|
var condition = parse_Expression()
|
||
|
eat(TokenType.ExpressionPEnd)
|
||
|
|
||
|
eat(TokenType.StatementEnd)
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.DoWhileStatement
|
||
|
node.Value = [ condition, body ]
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > IterationStatement
|
||
|
# ForStatement
|
||
|
# : for (
|
||
|
# OptForStatementInit ;
|
||
|
# OptExpression ;
|
||
|
# OptExpression
|
||
|
# )
|
||
|
# Statement
|
||
|
# ;
|
||
|
func parse_ForStatement():
|
||
|
eat(TokenType.For)
|
||
|
|
||
|
eat(TokenType.ExpressionPStart)
|
||
|
|
||
|
var init = null
|
||
|
var condition = null
|
||
|
var update = null
|
||
|
|
||
|
if NextToken.Type != TokenType.StatementEnd :
|
||
|
init = parse_ForStatementInit()
|
||
|
eat(TokenType.StatementEnd)
|
||
|
|
||
|
if NextToken.Type != TokenType.StatementEnd :
|
||
|
condition = parse_Expression()
|
||
|
eat(TokenType.StatementEnd)
|
||
|
|
||
|
if NextToken.Type != TokenType.ExpressionPEnd :
|
||
|
update = parse_Expression()
|
||
|
|
||
|
eat(TokenType.ExpressionPEnd)
|
||
|
|
||
|
var body = parse_Statement()
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.ForStatement
|
||
|
node.Value = [ init, condition, update, body ]
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > ForStatement
|
||
|
# ForStatementInit
|
||
|
# : VariableStatemetnInit
|
||
|
# | Expression
|
||
|
# ;
|
||
|
func parse_ForStatementInit():
|
||
|
if NextToken.Type == TokenType.VarDeclare :
|
||
|
return parse_VariableStatementInit()
|
||
|
|
||
|
return parse_Expression()
|
||
|
|
||
|
# > Statement
|
||
|
# FunctionDeclaration
|
||
|
# : FuncDeclare ( OptFomralParameterList ) BlockStatement
|
||
|
# ;
|
||
|
func parse_FunctionDeclaration():
|
||
|
|
||
|
eat(TokenType.FuncDeclare)
|
||
|
|
||
|
var name = parse_Identifier()
|
||
|
|
||
|
eat(TokenType.ExpressionPStart)
|
||
|
|
||
|
var params
|
||
|
if NextToken.Type != TokenType.ExpressionPEnd :
|
||
|
params = parse_FormalParameterList()
|
||
|
else :
|
||
|
params = []
|
||
|
|
||
|
eat(TokenType.ExpressionPEnd)
|
||
|
|
||
|
var body = parse_BlockStatement()
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.FunctionDeclaration
|
||
|
node.Value = [ name, params, body ]
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > FunctionDeclaration
|
||
|
# FormalParameterList
|
||
|
# : Identifier
|
||
|
# | FormalParameterList , Identifier
|
||
|
# ;
|
||
|
func parse_FormalParameterList():
|
||
|
var params = [ parse_Identifier() ]
|
||
|
|
||
|
while NextToken.Type == TokenType.CommaDelimiter :
|
||
|
eat(TokenType.CommaDelimiter)
|
||
|
params.append(parse_Identifier())
|
||
|
|
||
|
return params
|
||
|
|
||
|
# > Statement
|
||
|
# ReturnStatement
|
||
|
# : return OptExpression
|
||
|
# ;
|
||
|
func parse_ReturnStatement():
|
||
|
eat(TokenType.Return)
|
||
|
|
||
|
var argument = null
|
||
|
if NextToken.Type != TokenType.StatementEnd :
|
||
|
argument = parse_Expression()
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.ReturnStatement
|
||
|
node.Value = argument
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > Statement
|
||
|
# ExpressionStatement
|
||
|
# : Expression
|
||
|
# ;
|
||
|
func parse_ExpressionStatement():
|
||
|
var expression = parse_Expression()
|
||
|
eat(TokenType.StatementEnd)
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.ExpressionStatement
|
||
|
node.Value = expression
|
||
|
|
||
|
return expression
|
||
|
|
||
|
# > ExpressionStatement
|
||
|
# > If_Statement
|
||
|
# > WhileStatement
|
||
|
# > PrimaryExpression
|
||
|
# Expression
|
||
|
# : AssignmentExpression
|
||
|
# ;
|
||
|
func parse_Expression():
|
||
|
return parse_AssignmentExpression()
|
||
|
|
||
|
# > VariableStatement
|
||
|
# VariableDeclarationList
|
||
|
# : VariableDeclaration
|
||
|
# | VariableDelcarationList , VariableDeclaration -> VariableDelcaration , ...
|
||
|
func parse_VariableDeclarationList():
|
||
|
var \
|
||
|
declarations = []
|
||
|
declarations.append(parse_VariableDeclaration())
|
||
|
|
||
|
while NextToken.Type == TokenType.CommaDelimiter :
|
||
|
eat(TokenType.CommaDelimiter)
|
||
|
declarations.append(parse_VariableDeclaration())
|
||
|
|
||
|
return declarations
|
||
|
|
||
|
# > VariableDeclarationList
|
||
|
# VariableDeclaration
|
||
|
# : Identifier OptVariableInitalizer
|
||
|
# ;
|
||
|
func parse_VariableDeclaration():
|
||
|
var identifier = parse_Identifier()
|
||
|
var initalizer
|
||
|
if NextToken.Type != TokenType.StatementEnd \
|
||
|
&& NextToken.Type != TokenType.CommaDelimiter :
|
||
|
initalizer = parse_VariableInitializer()
|
||
|
else :
|
||
|
initalizer = null
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.VariableDeclaration
|
||
|
node.Value = [ identifier, initalizer ]
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > VariableDeclaration
|
||
|
# VariableInitializer
|
||
|
# : Assignment AssignmentExpression
|
||
|
# ;
|
||
|
func parse_VariableInitializer():
|
||
|
eat(TokenType.Assignment)
|
||
|
|
||
|
return parse_AssignmentExpression()
|
||
|
|
||
|
# > Expression
|
||
|
# > VariableInitializer
|
||
|
# > AssignmentExpression
|
||
|
# AssignmentExpression
|
||
|
# : RelationalExpression
|
||
|
# | ResolvedSymbol AssignmentOperator AssignmetnExpression
|
||
|
# ;
|
||
|
func parse_AssignmentExpression():
|
||
|
var left = parse_LogicalOrExpression()
|
||
|
|
||
|
if NextToken.Type != TokenType.Assignment \
|
||
|
&& NextToken.Type != TokenType.ComplexAssignment :
|
||
|
return left
|
||
|
|
||
|
var assignmentOp;
|
||
|
|
||
|
if NextToken.Type == TokenType.Assignment :
|
||
|
assignmentOp = eat(TokenType.Assignment)
|
||
|
elif NextToken.Type == TokenType.ComplexAssignment :
|
||
|
assignmentOp = eat(TokenType.ComplexAssignment)
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.AssignmentExpression
|
||
|
node.Value = \
|
||
|
[
|
||
|
assignmentOp.Value,
|
||
|
left,
|
||
|
parse_AssignmentExpression()
|
||
|
]
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > VariableDeclaration
|
||
|
# > ParenthesizedExpression
|
||
|
# Identifier
|
||
|
# : IdentifierSymbol
|
||
|
# ;
|
||
|
func parse_Identifier():
|
||
|
var name = eat(TokenType.Identifier).Value
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.Identifier
|
||
|
node.Value = name
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > AssignmentExpression
|
||
|
# Logical Or Expression
|
||
|
# : LogicalAndExpression Logical_Or LogicalOrExpression
|
||
|
# | LogicalOrExpression
|
||
|
# ;
|
||
|
func parse_LogicalOrExpression():
|
||
|
var \
|
||
|
parseFn = FuncRef.new()
|
||
|
parseFn.set_instance(self)
|
||
|
parseFn.set_function("pasre_LogicalAndExpression")
|
||
|
|
||
|
return parse_LogicalExpression(parseFn, TokenType.Logical_Or)
|
||
|
|
||
|
# > LogicaOrExpression
|
||
|
# Logical And Expression
|
||
|
# : EqualityExpression Logical_And LogicalAndExpression
|
||
|
# | EqualityExpression
|
||
|
# ;
|
||
|
func pasre_LogicalAndExpression():
|
||
|
var \
|
||
|
parseFn = FuncRef.new()
|
||
|
parseFn.set_instance(self)
|
||
|
parseFn.set_function("parse_EqualityExpression")
|
||
|
|
||
|
return parse_LogicalExpression(parseFn, TokenType.Logical_And)
|
||
|
|
||
|
# Equality Operators: ==, !=
|
||
|
#
|
||
|
# > LogicalAndExpression
|
||
|
# EqualityExpression
|
||
|
# : RelationalExpression EqualityOp RelationalExpression
|
||
|
# | RelationalExpression
|
||
|
# ;
|
||
|
func parse_EqualityExpression():
|
||
|
var \
|
||
|
parseFn = FuncRef.new()
|
||
|
parseFn.set_instance(self)
|
||
|
parseFn.set_function("parse_RelationalExpression")
|
||
|
|
||
|
return parse_BinaryExpression(parseFn, TokenType.EqualityOp)
|
||
|
|
||
|
# Relational Operators: >, >=, <, <=
|
||
|
#
|
||
|
# > EqualityExpression
|
||
|
# Relational Expression
|
||
|
# : AdditiveExpression
|
||
|
# | AdditiveExpression RelationalOp RelationalExpression
|
||
|
# ;
|
||
|
func parse_RelationalExpression():
|
||
|
var \
|
||
|
parseFn = FuncRef.new()
|
||
|
parseFn.set_instance(self)
|
||
|
parseFn.set_function("parse_AdditiveExpression")
|
||
|
|
||
|
return parse_BinaryExpression(parseFn, TokenType.RelationalOp)
|
||
|
|
||
|
# > RelationalExpression
|
||
|
# AdditiveExpression
|
||
|
# : MultiplicativeExpression
|
||
|
# | AdditiveExpression AdditiveOp MultiplicativeExpression -> MultiplicativeExpression AdditiveOp ... Literal
|
||
|
# ;
|
||
|
func parse_AdditiveExpression():
|
||
|
var \
|
||
|
parseFn = FuncRef.new()
|
||
|
parseFn.set_instance(self)
|
||
|
parseFn.set_function("parse_MultiplicativeExpression")
|
||
|
|
||
|
return parse_BinaryExpression(parseFn, TokenType.AdditiveOp)
|
||
|
|
||
|
# > AdditiveExpression
|
||
|
# MultiplicativeExpression
|
||
|
# : UnaryExpressioon
|
||
|
# : MultiplicativeExpression MultiplicativeOp UnaryExpression -> UnaryExpression MultiplicativeOp ... Literal
|
||
|
# ;
|
||
|
func parse_MultiplicativeExpression():
|
||
|
var \
|
||
|
parseFn = FuncRef.new()
|
||
|
parseFn.set_instance(self)
|
||
|
parseFn.set_function("parse_UnaryExpression")
|
||
|
|
||
|
return parse_BinaryExpression(parseFn, TokenType.MultiplicativeOp)
|
||
|
|
||
|
# > MultiplicativeExpression
|
||
|
# > UnaryExpression
|
||
|
# UnaryExpression
|
||
|
# : ResolvedSymbol
|
||
|
# | AdditiveOp UnaryExpression
|
||
|
# | Logical_Not UnaryExpression
|
||
|
# ;
|
||
|
func parse_UnaryExpression():
|
||
|
var operator
|
||
|
match NextToken.Type:
|
||
|
TokenType.AdditiveOp:
|
||
|
operator = eat(TokenType.AdditiveOp).Value
|
||
|
TokenType.Logical_Not:
|
||
|
operator = eat(TokenType.Logical_Not).Value
|
||
|
|
||
|
if operator == null :
|
||
|
return parse_ResolvedSymbol()
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.UnaryExpression
|
||
|
node.Value = [ operator, parse_UnaryExpression() ]
|
||
|
|
||
|
return node;
|
||
|
|
||
|
# > UnaryExpression
|
||
|
# > PrimaryExpression
|
||
|
# ResolvedSymbol (LeftHandExpression)
|
||
|
# : MemberExpression
|
||
|
# ;
|
||
|
func parse_ResolvedSymbol():
|
||
|
return parse_FunctionExpression()
|
||
|
|
||
|
# > ResolvedSymbol
|
||
|
# FunctionExpression
|
||
|
# : MemberExpression
|
||
|
# | CallExpression
|
||
|
# ;
|
||
|
func parse_FunctionExpression():
|
||
|
var member = parse_MemberExpression()
|
||
|
|
||
|
if NextToken.Type == TokenType.ExpressionPStart :
|
||
|
return parse_CallExpression(member)
|
||
|
|
||
|
return member
|
||
|
|
||
|
# > ResolvedSymbol
|
||
|
# MemberExpression
|
||
|
# : PrimaryExpression
|
||
|
# | MemberExpression . Identifier
|
||
|
# | MemberExpression [ Expression ]
|
||
|
# ;
|
||
|
func parse_MemberExpression():
|
||
|
var expression = parse_PrimaryExpression()
|
||
|
|
||
|
while NextToken.Type == TokenType.MemberDelimiter || NextToken.Type == TokenType.expr_SqBStart :
|
||
|
if NextToken.Type == TokenType.MemberDelimiter :
|
||
|
eat(TokenType.MemberDelimiter)
|
||
|
|
||
|
var member = parse_Identifier()
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.MemberExpression
|
||
|
node.Value = [ false, expression, member ]
|
||
|
|
||
|
expression = node
|
||
|
|
||
|
if NextToken.Type == TokenType.expr_SqBStart :
|
||
|
eat(TokenType.expr_SqBStart)
|
||
|
|
||
|
var sbExpression = parse_Expression()
|
||
|
|
||
|
eat(TokenType.expr_SqBEnd)
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.MemberExpression
|
||
|
node.Value = [ true, expression, sbExpression ]
|
||
|
|
||
|
expression = node
|
||
|
|
||
|
return expression
|
||
|
|
||
|
# > FunctionExpression
|
||
|
# CallExpression
|
||
|
# : Callee Arguments
|
||
|
# ;
|
||
|
#
|
||
|
# Callee
|
||
|
# : MemberExpression
|
||
|
# | CallExpression
|
||
|
# ;
|
||
|
func parse_CallExpression(callee):
|
||
|
var \
|
||
|
callExpression = SyntaxNode.new()
|
||
|
callExpression.Type = SyntaxNodeType.CallExpression
|
||
|
callExpression.Value = \
|
||
|
[
|
||
|
callee,
|
||
|
parse_Arguments()
|
||
|
]
|
||
|
|
||
|
if NextToken.Type == TokenType.ExpressionPStart :
|
||
|
callExpression = parse_CallExpression(callExpression)
|
||
|
|
||
|
return callExpression
|
||
|
|
||
|
# > CallExpression
|
||
|
# Arugments
|
||
|
# : ( OptArgumentList )
|
||
|
# ;
|
||
|
func parse_Arguments():
|
||
|
eat(TokenType.ExpressionPStart)
|
||
|
|
||
|
var argumentList = null
|
||
|
|
||
|
if NextToken.Type != TokenType.ExpressionPEnd :
|
||
|
argumentList = parse_ArgumentList()
|
||
|
|
||
|
eat(TokenType.ExpressionPEnd)
|
||
|
|
||
|
return argumentList
|
||
|
|
||
|
# > Arguments
|
||
|
# ArgumentList
|
||
|
# : AssignmentExpression
|
||
|
# | ArgumentList , AssignmentExpression
|
||
|
# ;
|
||
|
func parse_ArgumentList():
|
||
|
var argumentList = [ parse_AssignmentExpression() ]
|
||
|
|
||
|
while NextToken.Type == TokenType.CommaDelimiter :
|
||
|
eat(TokenType.CommaDelimiter)
|
||
|
|
||
|
argumentList.append( parse_AssignmentExpression() )
|
||
|
|
||
|
return argumentList
|
||
|
|
||
|
|
||
|
# > MemberExpression
|
||
|
# PrimaryExpression
|
||
|
# : Literal
|
||
|
# | ParenthesizedExpression
|
||
|
# ;
|
||
|
func parse_PrimaryExpression():
|
||
|
if is_Literal():
|
||
|
return parse_Literal()
|
||
|
|
||
|
match NextToken.Type:
|
||
|
TokenType.ExpressionPStart:
|
||
|
return parse_ParenthesizedExpression()
|
||
|
TokenType.Identifier:
|
||
|
var identifier = parse_Identifier()
|
||
|
|
||
|
if identifier.Type == SyntaxNodeType.Identifier :
|
||
|
return identifier
|
||
|
|
||
|
var assertStrTmplt = "parse_PrimaryExpression: (Identifier) Unexpected symbol: {value}"
|
||
|
var assertStr = assertStrTmplt.format({"value" : identifier.Type})
|
||
|
assert(true != true, assertStr)
|
||
|
|
||
|
return parse_ResolvedSymbol()
|
||
|
|
||
|
# > PrimaryExpression
|
||
|
# Literal
|
||
|
# : NumericLiteral
|
||
|
# | StringLiteral
|
||
|
# | BooleanLiteral
|
||
|
# | NullLiteral
|
||
|
# ;
|
||
|
func parse_Literal():
|
||
|
match NextToken.Type :
|
||
|
TokenType.Number:
|
||
|
return parse_NumericLiteral()
|
||
|
TokenType.String:
|
||
|
return parse_StringLiteral()
|
||
|
TokenType.Bool_true:
|
||
|
return parse_BooleanLiteral(TokenType.Bool_true)
|
||
|
TokenType.Bool_false:
|
||
|
return parse_BooleanLiteral(TokenType.Bool_false)
|
||
|
TokenType.NullValue:
|
||
|
return parse_NullLiteral()
|
||
|
|
||
|
assert(false, "parse_Literal: Was not able to detect valid literal type from NextToken")
|
||
|
|
||
|
# > PrimaryExpression
|
||
|
# ParenthesizedExpression
|
||
|
# : ( Expression )
|
||
|
# ;
|
||
|
func parse_ParenthesizedExpression():
|
||
|
eat(TokenType.ExpressionPStart)
|
||
|
|
||
|
var expression = parse_Expression()
|
||
|
|
||
|
eat(TokenType.ExpressionPEnd)
|
||
|
|
||
|
return expression
|
||
|
|
||
|
# > Literal
|
||
|
# NumericLiteral
|
||
|
# : Number
|
||
|
# ;
|
||
|
func parse_NumericLiteral():
|
||
|
var Token = eat(TokenType.Number)
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.NumericLiteral
|
||
|
node.Value = int( Token.Value )
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > Literal
|
||
|
# StringLiteral
|
||
|
# : String
|
||
|
# ;
|
||
|
func parse_StringLiteral():
|
||
|
var Token = eat(TokenType.String)
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.StringLiteral
|
||
|
node.Value = Token.Value.substr( 1, Token.Value.length() - 2 )
|
||
|
|
||
|
return node
|
||
|
|
||
|
|
||
|
# > Literal
|
||
|
# BooleanLiteral
|
||
|
# : true
|
||
|
# | false
|
||
|
# ;
|
||
|
func parse_BooleanLiteral(token):
|
||
|
eat(token)
|
||
|
var value
|
||
|
if (TokenType.Bool_true == token) :
|
||
|
value = true
|
||
|
elif (TokenType.Bool_false == token) :
|
||
|
value = false
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.BooleanLiteral
|
||
|
node.Value = value
|
||
|
|
||
|
return node
|
||
|
|
||
|
# > Literal
|
||
|
# NullLiteral
|
||
|
# : null
|
||
|
# ;
|
||
|
func parse_NullLiteral():
|
||
|
eat(TokenType.NullValue)
|
||
|
|
||
|
var \
|
||
|
node = SyntaxNode.new()
|
||
|
node.Type = SyntaxNodeType.NullLiteral
|
||
|
node.Value = null
|
||
|
|
||
|
return node
|
||
|
|
||
|
var GParser = Parser.new()
|
||
|
|
||
|
|
||
|
|
||
|
onready var TextOut = GScene.get_node("TextOutput")
|
||
|
|
||
|
func tout(text):
|
||
|
TextOut.insert_text_at_cursor(text)
|
||
|
|
||
|
const Tests = \
|
||
|
{
|
||
|
MultiStatement = \
|
||
|
{
|
||
|
Name = "Multi-Statement",
|
||
|
File = "1.Multi-Statement.uf"
|
||
|
},
|
||
|
BlockStatement = \
|
||
|
{
|
||
|
Name = "Block Statement",
|
||
|
File = "2.BlockStatement.uf"
|
||
|
},
|
||
|
BinaryExpression = \
|
||
|
{
|
||
|
Name = "Binary Expression",
|
||
|
File = "3.BinaryExpression.uf"
|
||
|
},
|
||
|
Assignment = \
|
||
|
{
|
||
|
Name = "Assignment",
|
||
|
File = "4.Assignment.uf"
|
||
|
},
|
||
|
VaraibleDeclaration = \
|
||
|
{
|
||
|
Name = "Variable Declaration",
|
||
|
File = "5.VariableDeclaration.uf"
|
||
|
},
|
||
|
Conditionals = \
|
||
|
{
|
||
|
Name = "Conditionals",
|
||
|
File = "6.Conditionals.uf"
|
||
|
},
|
||
|
Relations = \
|
||
|
{
|
||
|
Name = "Relations",
|
||
|
File = "7.Relations.uf"
|
||
|
},
|
||
|
Equality = \
|
||
|
{
|
||
|
Name = "Equality",
|
||
|
File = "8.Equality.uf"
|
||
|
},
|
||
|
Logical = \
|
||
|
{
|
||
|
Name = "Logical",
|
||
|
File = "9.Logical.uf"
|
||
|
},
|
||
|
Unary = \
|
||
|
{
|
||
|
Name = "Unary",
|
||
|
File = "10.Unary.uf"
|
||
|
},
|
||
|
Iteration = \
|
||
|
{
|
||
|
Name = "Iteration",
|
||
|
File = "11.Iteration.uf"
|
||
|
},
|
||
|
Functions = \
|
||
|
{
|
||
|
Name = "Functions",
|
||
|
File = "12.Functions.uf"
|
||
|
},
|
||
|
MemberExpressions = \
|
||
|
{
|
||
|
Name = "Member Expressions",
|
||
|
File = "13.MemberExpressions.uf"
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func test(entry):
|
||
|
var introMessage = "Testing: {Name}\n"
|
||
|
var introMessageFormatted = introMessage.format({"Name" : entry.Name})
|
||
|
tout(introMessageFormatted)
|
||
|
|
||
|
var path
|
||
|
if Engine.editor_hint :
|
||
|
path = "res://../Tests/{TestName}"
|
||
|
else :
|
||
|
path = "res://../Builds/Tests/{TestName}"
|
||
|
var pathFormatted = path.format({"TestName" : entry.File})
|
||
|
|
||
|
var \
|
||
|
file = File.new()
|
||
|
file.open(pathFormatted, File.READ)
|
||
|
|
||
|
var programDescription = file.get_as_text()
|
||
|
file.close()
|
||
|
|
||
|
GTokenizer.init(programDescription)
|
||
|
var ast = GParser.parse(GTokenizer)
|
||
|
|
||
|
var json = JSON.print(ast.to_Dictionary(), '\t')
|
||
|
|
||
|
tout(json + "\n")
|
||
|
tout("Passed!\n")
|
||
|
|
||
|
|
||
|
# Main Entry point.
|
||
|
func _ready():
|
||
|
for Key in Tests :
|
||
|
test(Tests[Key])
|