mirror of
https://github.com/Ed94/LangStudies.git
synced 2025-06-15 11:31:46 -07:00
EoI : Lectures 1-5 complete
This commit is contained in:
@ -0,0 +1,38 @@
|
||||
extends Node
|
||||
|
||||
var eva = preload("Eva.gd").new()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# UX --------------------------------------------------------
|
||||
onready var Editor = get_node("Editor_TEdit")
|
||||
onready var Output = get_node("Output_TEdit")
|
||||
onready var Eva_Btn = get_node("VBox/Eva_Interpret_Btn")
|
||||
onready var Back_Btn = get_node("VBox/Back_Btn")
|
||||
|
||||
|
||||
func evaBtn_pressed():
|
||||
eva.init(Editor.text)
|
||||
|
||||
var ast = eva.parse()
|
||||
|
||||
Output.text = eva.eval(ast)
|
||||
|
||||
func backBtn_pressed():
|
||||
queue_free()
|
||||
|
||||
|
||||
func _ready():
|
||||
Eva_Btn.connect("pressed", self, "evaBtn_pressed")
|
||||
Back_Btn.connect("pressed", self, "backBtn_pressed")
|
||||
|
316
App/EoI/Scripts/Eva.gd
Normal file
316
App/EoI/Scripts/Eva.gd
Normal file
@ -0,0 +1,316 @@
|
||||
extends Object
|
||||
|
||||
var SRegEx = preload("res://RegM/Scripts/SRegex.gd").new()
|
||||
|
||||
|
||||
const TType : Dictionary = \
|
||||
{
|
||||
fmt_S = "Formatting",
|
||||
cmt_SL = "Comment Single-Line",
|
||||
cmt_ML = "Comment Multi-Line",
|
||||
|
||||
def_Start = "Expression Start",
|
||||
def_End = "Expression End",
|
||||
|
||||
literal_Number = "LIteral: Number",
|
||||
literal_String = "Literal: String",
|
||||
|
||||
operator = "Operator"
|
||||
}
|
||||
|
||||
const Spec : Dictionary = \
|
||||
{
|
||||
TType.cmt_SL : "start // inline.repeat(0-)",
|
||||
TType.cmt_ML : "start /* set(whitespace !whitespace).repeat(0-).lazy */",
|
||||
|
||||
TType.fmt_S : "start whitespace.repeat(1-).lazy",
|
||||
|
||||
TType.def_Start : "start \\(",
|
||||
TType.def_End : "start \\)",
|
||||
|
||||
TType.literal_Number : \
|
||||
"""start
|
||||
set(+ \\-).repeat(0-1)
|
||||
( set(0-9).repeat(1-) \\. ).repeat(0-1)
|
||||
set(0-9).repeat(1-)
|
||||
""",
|
||||
TType.literal_String : "start \\\" !set( \\\" ).repeat(0-) \\\" ",
|
||||
|
||||
TType.operator : "start set(+ \\-)",
|
||||
}
|
||||
|
||||
class Token:
|
||||
var Type : String
|
||||
var Value : String
|
||||
|
||||
func is_Literal():
|
||||
return Type == TType.literal_Number || Type == TType.literal_String;
|
||||
|
||||
|
||||
var SourceText : String
|
||||
var Cursor : int
|
||||
var SpecRegex : Dictionary
|
||||
var Tokens : Array
|
||||
var TokenIndex : int = 0
|
||||
|
||||
|
||||
func compile_regex():
|
||||
for type in TType.values() :
|
||||
var \
|
||||
regex = RegEx.new()
|
||||
regex.compile( SRegEx.transpile(Spec[type]) )
|
||||
|
||||
SpecRegex[type] = regex
|
||||
|
||||
func init(programSrcText):
|
||||
SourceText = programSrcText
|
||||
Cursor = 0
|
||||
TokenIndex = 0
|
||||
|
||||
if SpecRegex.size() == 0 :
|
||||
compile_regex()
|
||||
|
||||
tokenize()
|
||||
|
||||
func next_Token():
|
||||
|
||||
var nextToken = null
|
||||
|
||||
if Tokens.size() > TokenIndex :
|
||||
nextToken = Tokens[TokenIndex]
|
||||
TokenIndex += 1
|
||||
|
||||
return nextToken
|
||||
|
||||
func reached_EndOfText():
|
||||
return Cursor >= SourceText.length()
|
||||
|
||||
func tokenize():
|
||||
Tokens.clear()
|
||||
|
||||
while reached_EndOfText() == false :
|
||||
var srcLeft = SourceText.substr(Cursor)
|
||||
var token = Token.new()
|
||||
|
||||
var error = true
|
||||
for type in TType.values() :
|
||||
var result = SpecRegex[type].search( srcLeft )
|
||||
if result == null || result.get_start() != 0 :
|
||||
continue
|
||||
|
||||
# Skip Comments
|
||||
if type == TType.cmt_SL || type == TType.cmt_ML :
|
||||
Cursor += result.get_string().length()
|
||||
error = false
|
||||
break
|
||||
|
||||
# Skip Whitespace
|
||||
if type == TType.fmt_S :
|
||||
var addVal = result.get_string().length()
|
||||
|
||||
Cursor += addVal
|
||||
error = false
|
||||
break
|
||||
|
||||
token.Type = type
|
||||
token.Value = result.get_string()
|
||||
Cursor += ( result.get_string().length() )
|
||||
|
||||
Tokens.append( token )
|
||||
|
||||
error = false
|
||||
break;
|
||||
|
||||
if error :
|
||||
var assertStrTmplt = "next_token: Source text not understood by tokenizer at Cursor pos: {value} -: {txt}"
|
||||
var assertStr = assertStrTmplt.format({"value" : Cursor, "txt" : srcLeft})
|
||||
assert(true != true, assertStr)
|
||||
return
|
||||
# ---------------------------------------------------------- Lexer
|
||||
|
||||
|
||||
# ---------------------------------------------------------- Parser
|
||||
# ---------------------------------------------------------- AST Node
|
||||
|
||||
const NType = \
|
||||
{
|
||||
literal_Number = "Literal: Number",
|
||||
literal_String = "Literal: String",
|
||||
|
||||
op_Add = "+",
|
||||
op_Mult = "*"
|
||||
}
|
||||
|
||||
class ASTNode:
|
||||
var Data : Array
|
||||
|
||||
func add_Expr( expr ):
|
||||
Data.append(expr)
|
||||
|
||||
func add_TokenValue( token ):
|
||||
Data.append( token.Value )
|
||||
|
||||
func set_Type( nType ):
|
||||
Data.append(nType)
|
||||
|
||||
func arg( id ):
|
||||
return Data[id]
|
||||
|
||||
func num_args():
|
||||
return Data.size() - 1
|
||||
|
||||
func type():
|
||||
return Data[0]
|
||||
|
||||
func is_Number():
|
||||
return type() == NType.literal_Number
|
||||
|
||||
func is_String():
|
||||
return type() == NType.literal_String
|
||||
|
||||
func string():
|
||||
return arg(1).substr(1, arg(1).length() -2)
|
||||
|
||||
|
||||
# Serialization ----------------------------------------------------
|
||||
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 = []
|
||||
|
||||
# if typeof(Value) == TYPE_ARRAY :
|
||||
var \
|
||||
to_SExpression_Fn = FuncRef.new()
|
||||
to_SExpression_Fn.set_function("to_SExpression")
|
||||
|
||||
return array_Serialize( self.Data, to_SExpression_Fn )
|
||||
|
||||
# if typeof(Value) == TYPE_OBJECT :
|
||||
# var result = [ Type, Value.to_SExpression() ]
|
||||
# return result
|
||||
|
||||
# expression.append(Value)
|
||||
# return expression
|
||||
# Serialization END -------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------- AST Node END
|
||||
|
||||
var TokenType : Token
|
||||
var NextToken : Token
|
||||
|
||||
# Gets the next token only if the current token is the specified intended token (tokenType)
|
||||
func eat(tokenType):
|
||||
var currToken = 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 = next_Token()
|
||||
|
||||
return currToken
|
||||
|
||||
func parse():
|
||||
NextToken = next_Token()
|
||||
|
||||
if NextToken.Type == TType.def_Start:
|
||||
return parse_Expression()
|
||||
|
||||
if NextToken.is_Literal():
|
||||
return parse_Literal()
|
||||
|
||||
func parse_Expression():
|
||||
eat(TType.def_Start)
|
||||
var node : ASTNode
|
||||
|
||||
if NextToken.Type == TType.operator:
|
||||
node = parse_Operator()
|
||||
|
||||
var arg = 1
|
||||
while NextToken.Type != TType.def_End:
|
||||
node.add_Expr( parse_Literal() )
|
||||
|
||||
if NextToken.is_Literal():
|
||||
node = parse_Literal()
|
||||
|
||||
eat(TType.def_End)
|
||||
|
||||
return node
|
||||
|
||||
func parse_Operator():
|
||||
var \
|
||||
node = ASTNode.new()
|
||||
|
||||
match NextToken.Value:
|
||||
NType.op_Add:
|
||||
node.set_Type(NType.op_Add)
|
||||
NType.op_Mult:
|
||||
node.set_Type(NType.op_Mult)
|
||||
|
||||
eat(TType.operator)
|
||||
|
||||
return node
|
||||
|
||||
func parse_Literal():
|
||||
var \
|
||||
node = ASTNode.new()
|
||||
|
||||
match NextToken.Type:
|
||||
TType.literal_Number:
|
||||
node.set_Type(NType.literal_Number)
|
||||
node.add_TokenValue(NextToken)
|
||||
|
||||
eat(TType.literal_Number)
|
||||
|
||||
TType.literal_String:
|
||||
node.set_Type(NType.literal_String)
|
||||
node.add_TokenValue(NextToken)
|
||||
|
||||
eat(TType.literal_String)
|
||||
|
||||
return node
|
||||
|
||||
# ---------------------------------------------------------- Parser END
|
||||
|
||||
class_name Eva
|
||||
|
||||
# ---------------------------------------------------------- GLOBALS
|
||||
|
||||
# ---------------------------------------------------------- GLOBALS END
|
||||
|
||||
# ---------------------------------------------------------- UTILITIES
|
||||
|
||||
func throw( message ):
|
||||
assert(false, message)
|
||||
# ---------------------------------------------------------- UTILITIES END
|
||||
|
||||
func eval( ast ):
|
||||
if ast.is_Number() :
|
||||
return float(ast.arg(1))
|
||||
if ast.is_String() :
|
||||
return ast.string()
|
||||
|
||||
if ast.type() == NType.op_Add:
|
||||
return String(eval( ast.arg(1) ) + eval( ast.arg(2) ))
|
||||
|
||||
if ast.type() == NType.op_Mult:
|
||||
return String(eval( ast.arg(1) ) * eval( ast.arg(2) ))
|
||||
|
||||
throw("Unimplemented")
|
Reference in New Issue
Block a user