Skip to content

Commit

Permalink
Refactor parser and lexer: update token handling for maps, enhance er…
Browse files Browse the repository at this point in the history
…ror reporting, and add support for new syntax features
  • Loading branch information
itsfuad committed Dec 6, 2024
1 parent 33231e4 commit 7ce0291
Show file tree
Hide file tree
Showing 30 changed files with 8,163 additions and 135 deletions.
9 changes: 2 additions & 7 deletions analyzer/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func (t *TypeEnvironment) resolveFunctionEnv() (*TypeEnvironment, error) {
return t, nil
}
if t.parent == nil {
return nil, fmt.Errorf("function not found")
return nil, fmt.Errorf("no function found in this scope")
}
return t.parent.resolveFunctionEnv()
}
Expand All @@ -119,7 +119,7 @@ func (t *TypeEnvironment) declareVar(name string, typeVar TcValue, isConst bool,
}

if ok, hasVal := builtinValues[name]; hasVal && ok {
return fmt.Errorf("cannot declare builtin value '%s'", name)
return fmt.Errorf("cannot redeclare builtin value '%s'", name)
}

//should not be declared
Expand Down Expand Up @@ -158,11 +158,6 @@ func getTypeDefinition(name string) (TcValue, error) {
if typ, ok := typeDefinitions[name]; !ok {
return nil, fmt.Errorf("unknown type '%s'", name)
} else {
if tp, ok := typ.(UserDefined); ok {
if st, ok := tp.TypeDef.(Struct); ok {
fmt.Printf("unwrapping type from: %s\n", st.StructName)
}
}
return unwrapType(typ), nil
}
}
Expand Down
2 changes: 1 addition & 1 deletion analyzer/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func getFunctionReturnValue(env *TypeEnvironment, returnNode ast.Node) TcValue {
funcParent, err := env.resolveFunctionEnv()

if err != nil {
errgen.AddError(env.filePath, returnNode.StartPos().Line, returnNode.EndPos().Line, returnNode.StartPos().Column, returnNode.EndPos().Column, err.Error(), errgen.ERROR_NORMAL)
errgen.AddError(env.filePath, returnNode.StartPos().Line, returnNode.EndPos().Line, returnNode.StartPos().Column, returnNode.EndPos().Column, err.Error(), errgen.ERROR_CRITICAL)
}

fnName := funcParent.scopeName
Expand Down
6 changes: 0 additions & 6 deletions analyzer/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,8 @@ import (
func checkMapLiteral(node ast.MapLiteral, env *TypeEnvironment) TcValue {

//get the map definitions

evaluatedMapType := evaluateTypeName(node.MapType, env)

//check if the map type is valid
if evaluatedMapType.DType() != MAP_TYPE {
errgen.AddError(env.filePath, node.StartPos().Line, node.EndPos().Line, node.StartPos().Column, node.EndPos().Column, "invalid map type", errgen.ERROR_NORMAL)
}

//check the key value pairs
for _, value := range node.Values {
keyType := CheckAST(value.Key, env)
Expand Down
3 changes: 1 addition & 2 deletions analyzer/return.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import (
func checkReturnStmt(returnNode ast.ReturnStmt, env *TypeEnvironment) TcValue {
//check if the function is declared
if env.scopeType != FUNCTION_SCOPE {

errgen.AddError(env.filePath, returnNode.StartPos().Line, returnNode.EndPos().Line, returnNode.StartPos().Column, returnNode.EndPos().Column, "Return statement must be inside a function", errgen.ERROR_NORMAL)
errgen.AddError(env.filePath, returnNode.StartPos().Line, returnNode.EndPos().Line, returnNode.StartPos().Column, returnNode.EndPos().Column, "Return statement must be inside a function", errgen.ERROR_CRITICAL)
}

//check if the return type matches the function return type
Expand Down
8 changes: 6 additions & 2 deletions analyzer/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ func checkStructLiteral(structLit ast.StructLiteral, env *TypeEnvironment) TcVal

Type, err := getTypeDefinition(sName.Name) // need to get the most deep type
if err != nil {
errgen.AddError(env.filePath, sName.StartPos().Line, sName.EndPos().Line, sName.StartPos().Column, sName.EndPos().Column, fmt.Sprintf("'%s' is not a struct", sName.Name), errgen.ERROR_NORMAL)
errgen.AddError(env.filePath, sName.StartPos().Line, sName.EndPos().Line, sName.StartPos().Column, sName.EndPos().Column, err.Error(), errgen.ERROR_NORMAL)
}

structType := Type.(Struct)
structType, ok := Type.(Struct)

if !ok {
errgen.AddError(env.filePath, sName.StartPos().Line, sName.EndPos().Line, sName.StartPos().Column, sName.EndPos().Column, fmt.Sprintf("'%s' is not a struct", sName.Name), errgen.ERROR_CRITICAL)
}

// now we match the defined props with the provided props
for propname, prop := range structLit.Properties {
Expand Down
121 changes: 79 additions & 42 deletions analyzer/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,52 +89,89 @@ func isIntType(operand TcValue) bool {
func evaluateTypeName(dtype ast.DataType, env *TypeEnvironment) TcValue {
switch t := dtype.(type) {
case ast.ArrayType:
val := evaluateTypeName(t.ArrayType, env)
arr := Array{
DataType: builtins.ARRAY,
ArrayType: val,
}
return arr
return evalArray(t, env)
case ast.FunctionType:
var params []FnParam
for _, param := range t.Parameters {
paramType := evaluateTypeName(param.Type, env)
params = append(params, FnParam{
Name: param.Identifier.Name,
IsOptional: param.IsOptional,
Type: paramType,
})
}

returns := evaluateTypeName(t.ReturnType, env)

scope := NewTypeENV(env, FUNCTION_SCOPE, fmt.Sprintf("_FN_%s", RandStringRunes(10)), env.filePath)

return Fn{
DataType: builtins.FUNCTION,
Params: params,
Returns: returns,
FunctionScope: *scope,
}
return evalFn(t, env)
case ast.MapType:
keyType := evaluateTypeName(t.KeyType, env)
valueType := evaluateTypeName(t.ValueType, env)
return NewMap(keyType, valueType)
return evalMap(t, env)
case ast.UserDefinedType:
typename := t.AliasName
val, err := getTypeDefinition(typename) // need to get the most deep type
if err != nil || val == nil {
errgen.AddError(env.filePath, dtype.StartPos().Line, dtype.EndPos().Line, dtype.StartPos().Column, dtype.EndPos().Column, err.Error(), errgen.ERROR_CRITICAL)
}
return val
return evalUD(t, env)
case nil:
return NewVoid()
default:
val, err := getTypeDefinition(string(t.Type())) // need to get the most deep type
if err != nil || val == nil {
errgen.AddError(env.filePath, dtype.StartPos().Line, dtype.EndPos().Line, dtype.StartPos().Column, dtype.EndPos().Column, err.Error(), errgen.ERROR_CRITICAL)
return evalDefaultType(dtype, env)
}
}

func evalDefaultType(defaultType ast.DataType, env *TypeEnvironment) TcValue {
val, err := getTypeDefinition(string(defaultType.Type())) // need to get the most deep type
if err != nil || val == nil {
errgen.AddError(env.filePath, defaultType.StartPos().Line, defaultType.EndPos().Line, defaultType.StartPos().Column, defaultType.EndPos().Column, err.Error(), errgen.ERROR_CRITICAL)
}
return val
}

func evalUD(analyzedUD ast.UserDefinedType, env *TypeEnvironment) TcValue {
typename := analyzedUD.AliasName
val, err := getTypeDefinition(typename) // need to get the most deep type
if err != nil || val == nil {
errgen.AddError(env.filePath, analyzedUD.StartPos().Line, analyzedUD.EndPos().Line, analyzedUD.StartPos().Column, analyzedUD.EndPos().Column, err.Error(), errgen.ERROR_CRITICAL)
}
return val
}

func evalArray(analyzedArray ast.ArrayType, env *TypeEnvironment) TcValue {
val := evaluateTypeName(analyzedArray.ArrayType, env)
arr := Array{
DataType: builtins.ARRAY,
ArrayType: val,
}
return arr
}

func evalFn(analyzedFunctionType ast.FunctionType, env *TypeEnvironment) TcValue {
var params []FnParam
for _, param := range analyzedFunctionType.Parameters {
paramType := evaluateTypeName(param.Type, env)
params = append(params, FnParam{
Name: param.Identifier.Name,
IsOptional: param.IsOptional,
Type: paramType,
})
}

returns := evaluateTypeName(analyzedFunctionType.ReturnType, env)

scope := NewTypeENV(env, FUNCTION_SCOPE, fmt.Sprintf("_FN_%s", RandStringRunes(10)), env.filePath)

return Fn{
DataType: builtins.FUNCTION,
Params: params,
Returns: returns,
FunctionScope: *scope,
}
}

func evalMap(analyzedMap ast.MapType, env *TypeEnvironment) TcValue {
if analyzedMap.Map.Name == "map" {
keyType := evaluateTypeName(analyzedMap.KeyType, env)
valueType := evaluateTypeName(analyzedMap.ValueType, env)
return NewMap(keyType, valueType)
} else {
//find the name in the type definition

val, err := getTypeDefinition(analyzedMap.Map.Name) // need to get the most deep type
if err != nil {
errgen.AddError(env.filePath, analyzedMap.StartPos().Line, analyzedMap.EndPos().Line, analyzedMap.StartPos().Column, analyzedMap.EndPos().Column, err.Error(), errgen.ERROR_NORMAL)
}
return val

if mapVal, ok := val.(Map); ok {
return NewMap(mapVal.KeyType, mapVal.ValueType)
}

errgen.AddError(env.filePath, analyzedMap.StartPos().Line, analyzedMap.EndPos().Line, analyzedMap.StartPos().Column, analyzedMap.EndPos().Column, fmt.Sprintf("'%s' is not a map", analyzedMap.Map.Name), errgen.ERROR_CRITICAL)

return NewVoid()
}
}

Expand All @@ -148,7 +185,7 @@ func matchTypes(expectedType, providedType TcValue) error {
providedStr := tcValueToString(providedType)

if expectedStr != providedStr {
return fmt.Errorf("cannot assign type '%s' to type '%s'", providedStr, expectedStr)
return fmt.Errorf("cannot assign value of type '%s' to type '%s'", providedStr, expectedStr)
}

return nil
Expand Down Expand Up @@ -193,8 +230,8 @@ func tcValueToString(val TcValue) string {
func checkMethodsImplementations(expected, provided TcValue) error {

//check if the provided type implements the interface
expectedMethods := expected.(Interface).Methods
structType, ok := provided.(Struct)
expectedMethods := unwrapType(expected).(Interface).Methods
structType, ok := unwrapType(provided).(Struct)
if !ok {
return fmt.Errorf("type must be a struct")
}
Expand Down
3 changes: 2 additions & 1 deletion ast/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ func (a FunctionType) EndPos() lexer.Position {

type MapType struct {
TypeName builtins.PARSER_TYPE
Map IdentifierExpr
KeyType DataType
ValueType DataType
Location
Expand All @@ -209,7 +210,7 @@ func (a MapType) EndPos() lexer.Position {
}

type UserDefinedType struct {
TypeName builtins.PARSER_TYPE
TypeName builtins.PARSER_TYPE
AliasName string
Location
}
Expand Down
2 changes: 1 addition & 1 deletion builtins/commonTypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const (
)

type Searchable interface {
TC_TYPE | PARSER_TYPE
~string
}

func GetBitSize[T Searchable](kind T) uint8 {
Expand Down
14 changes: 14 additions & 0 deletions code/arrays.wal
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//Arrays are simple to create and are dynamic. Which means you can add or remove elements from an array at any time.
//Arrays are created using square brackets [] along with the type of the elements in the array. for example: []Type
let oneDArray : []i32 = [1, 2, 3];
//you can also remove the type as it can be inferred from the elements in the array
let twoDArray := [[1, 2], [3, 4]];
//you can also create an empty array and add elements to it later. But you need to specify the type of the elements in the array
let emptyArray : []i32;
emptyArray = [1, 2, 3];

//Indexing an array is done using square brackets [] with the index of the element you want to access
let firstElement := oneDArray[0]; //firstElement is 1 of type i32
let otherElement := twoDArray[1][0]; //otherElement is 3 of type i32
//you can also change the value of an element in an array using the index
oneDArray[0] = 5;
Loading

0 comments on commit 7ce0291

Please sign in to comment.