diff --git a/.gitignore b/.gitignore index 538724c..0ccf885 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ examples/* !examples/*.tsh ext +tests/std diff --git a/converters/bash/converter.go b/converters/bash/converter.go index c388c71..ea477af 100644 --- a/converters/bash/converter.go +++ b/converters/bash/converter.go @@ -9,21 +9,31 @@ import ( "github.com/monstermichl/typeshell/transpiler" ) +type helperName = string + +const ( + sliceAssignmentHelper helperName = "_sah" // Slice assignment + sliceCopyHelper helperName = "_sch" // Slice copy + structAssignmentHelper helperName = "_stah" // Struct assignment + stringSubscriptHelper helperName = "_stsh" // String subscript +) + type funcInfo struct { name string } type converter struct { - interpreter string - startCode []string - code []string - varCounter int - forCounter int - funcs []funcInfo - funcCounter int - sliceAssignmentHelperRequired bool - sliceCopyHelperRequired bool - stringSubscriptHelperRequired bool + interpreter string + startCode []string + code []string + varCounter int + forCounter int + funcs []funcInfo + funcCounter int + sliceAssignmentHelperRequired bool + structAssignmentHelperRequired bool + sliceCopyHelperRequired bool + stringSubscriptHelperRequired bool } func New() *converter { @@ -62,7 +72,7 @@ func (c *converter) ProgramEnd() error { // $2: Assignment index // $3: Assignment value // $4: Default value - c.addHelper("slice assignment", "_sah", + c.addHelper("slice assignment", sliceAssignmentHelper, "local _i=${2}", fmt.Sprintf(`local _l=%s`, c.sliceLenString("${1}")), `for ((_c=${_l};_c<${_i};_c++)); do`, @@ -73,7 +83,7 @@ func (c *converter) ProgramEnd() error { } if c.sliceCopyHelperRequired { - c.addHelper("slice copy", "_sch", + c.addHelper("slice copy", sliceCopyHelper, "local _i=0", fmt.Sprintf(`local _l=%s`, c.sliceLenString("${2}")), "local _n=$(eval \"echo \\${${1}}\")", @@ -85,8 +95,17 @@ func (c *converter) ProgramEnd() error { ) } + if c.structAssignmentHelperRequired { + // $1: Struct name + // $2: Assignment field + // $3: Assignment value + c.addHelper("struct assignment", structAssignmentHelper, + c.structAssignmentString("${1}", "${2}", "${3}", false), + ) + } + if c.stringSubscriptHelperRequired { - c.addHelper("substring", "_ssh", + c.addHelper("substring", stringSubscriptHelper, `_ls=$((${2}))`, `_ll=$(((${3}-${2})+1))`, `_ret="${1:${_ls}:${_ll}}"`, @@ -106,7 +125,13 @@ func (c *converter) VarAssignment(name string, value string, global bool) error func (c *converter) SliceAssignment(name string, index string, value string, defaultValue string, global bool) error { c.sliceAssignmentHelperRequired = true - c.addLine(fmt.Sprintf(`_sah %s %s "%s" "%s"`, c.varEvaluationString(name, global), index, value, defaultValue)) + c.callFunc(sliceAssignmentHelper, c.varEvaluationString(name, global), index, value, defaultValue) + return nil +} + +func (c *converter) StructAssignment(name string, field string, value string, global bool) error { + c.structAssignmentHelperRequired = true + c.callFunc(structAssignmentHelper, c.varEvaluationString(name, global), field, value) return nil } @@ -259,8 +284,8 @@ func (c *converter) BinaryOperation(left string, operator parser.BinaryOperator, return notAllowedError() } - switch valueType.DataType() { - case parser.DATA_TYPE_INTEGER: + switch valueType.Type().ElementaryType().Kind() { + case parser.TypeKindInt: switch operator { case parser.BINARY_OPERATOR_MULTIPLICATION, parser.BINARY_OPERATOR_DIVISION, @@ -272,7 +297,7 @@ func (c *converter) BinaryOperation(left string, operator parser.BinaryOperator, return notAllowedError() } c.VarAssignment(helper, fmt.Sprintf("$((%s%s%s))", left, operator, right), false) // Backslash is required for * operator to prevent pattern expansion (https://www.shell-tips.com/bash/math-arithmetic-calculation/#using-the-expr-command-line). - case parser.DATA_TYPE_STRING: + case parser.TypeKindString: switch operator { case parser.BINARY_OPERATOR_ADDITION: c.VarAssignment(helper, fmt.Sprintf("\"%s%s\"", left, right), false) @@ -289,15 +314,15 @@ func (c *converter) Comparison(left string, operator parser.CompareOperator, rig var operatorString string if !valueType.IsSlice() { - switch valueType.DataType() { - case parser.DATA_TYPE_BOOLEAN: + switch valueType.Type().ElementaryType().Kind() { + case parser.TypeKindBool: switch operator { case parser.COMPARE_OPERATOR_EQUAL: operatorString = "-eq" case parser.COMPARE_OPERATOR_NOT_EQUAL: operatorString = "-ne" } - case parser.DATA_TYPE_INTEGER: + case parser.TypeKindInt: switch operator { case parser.COMPARE_OPERATOR_EQUAL: operatorString = "-eq" @@ -312,7 +337,7 @@ func (c *converter) Comparison(left string, operator parser.CompareOperator, rig case parser.COMPARE_OPERATOR_LESS_OR_EQUAL: operatorString = "-le" } - case parser.DATA_TYPE_STRING: + case parser.TypeKindString: switch operator { case parser.COMPARE_OPERATOR_EQUAL: operatorString = "==" @@ -376,9 +401,7 @@ func (c *converter) VarEvaluation(name string, valueUsed bool, global bool) (str } func (c *converter) SliceInstantiation(values []string, valueUsed bool) (string, error) { - c.addLine(fmt.Sprintf(`_dvc=$((%s+1))`, c.varEvaluationString("_dvc", true))) // Dynamic variable counter. - helper := c.nextHelperVar() - c.VarAssignment(helper, fmt.Sprintf(`_dv%s`, c.varEvaluationString("_dvc", true)), false) + helper := c.nextDynamicHelperVar() if len(values) > 0 { vals := "" @@ -408,10 +431,30 @@ func (c *converter) SliceLen(name string, valueUsed bool) (string, error) { return c.VarEvaluation(helper, valueUsed, false) } +func (c *converter) StructInitialization(values []transpiler.StructValue, valueUsed bool) (string, error) { + helper := c.nextDynamicHelperVar() + + for _, value := range values { + c.addLine(c.structAssignmentString(c.varEvaluationString(helper, false), value.Name(), value.Value(), false)) + } + return c.varEvaluationString(helper, false), nil +} + +func (c *converter) StructEvaluation(name string, field string, valueUsed bool) (string, error) { + helper := c.nextHelperVar() + + c.VarAssignment( + helper, + c.structEvaluationString(name, field), + false, + ) + return c.VarEvaluation(helper, valueUsed, false) +} + func (c *converter) StringSubscript(value string, startIndex string, endIndex string, valueUsed bool) (string, error) { helper := c.nextHelperVar() - c.addLine(fmt.Sprintf(`_ssh "%s" %s %s`, value, startIndex, endIndex)) + c.callFunc(stringSubscriptHelper, value, startIndex, endIndex) c.VarAssignment(helper, c.varEvaluationString("_ret", true), false) // https://www.baeldung.com/linux/bash-substring#1-using-thecut-command c.stringSubscriptHelperRequired = true @@ -511,7 +554,7 @@ func (c *converter) Input(prompt string, valueUsed bool) (string, error) { func (c *converter) Copy(destination string, source string, valueUsed bool, global bool) (string, error) { destination = c.varName(destination, global) - c.addLine(fmt.Sprintf("_sch %s %s", destination, source)) + c.callFunc(sliceCopyHelper, destination, source) c.sliceAssignmentHelperRequired = true c.sliceCopyHelperRequired = true @@ -541,6 +584,10 @@ func (c *converter) ReadFile(path string, valueUsed bool) (string, error) { return c.VarEvaluation(helper, valueUsed, false) } +func (c *converter) callFunc(name string, args ...string) { + c.addLine(fmt.Sprintf(`%s "%s"`, name, strings.Join(args, `" "`))) +} + func (c *converter) mustCurrentForVar() string { return fmt.Sprintf("_fv%d", c.forCounter) } @@ -567,7 +614,11 @@ func (c *converter) varAssignmentString(name string, value string, global bool) } func (c *converter) varEvaluationString(name string, global bool) string { - return fmt.Sprintf("${%s}", c.varName(name, global)) + // Only evaluate if it's not already evaluated. + if !strings.HasPrefix(name, "${") && !strings.HasSuffix(name, "}") { + return fmt.Sprintf("${%s}", c.varName(name, global)) + } + return name } func (c *converter) sliceAssignmentString(name string, index string, value string, global bool) string { @@ -575,6 +626,10 @@ func (c *converter) sliceAssignmentString(name string, index string, value strin return fmt.Sprintf(`eval "%s[%s]=\"%s\""`, name, index, value) } +func (c *converter) structAssignmentString(name string, field string, value string, global bool) string { + return fmt.Sprintf(`eval "%s_%s=\"%s\""`, name, field, value) +} + func (c *converter) sliceEvaluationString(name string, index string) string { return fmt.Sprintf(`$(eval "echo \${%s[%s]}")`, name, index) } @@ -583,6 +638,10 @@ func (c *converter) sliceLenString(name string) string { return fmt.Sprintf(`$(eval "echo \${#%s[@]}")`, name) } +func (c *converter) structEvaluationString(name string, field string) string { + return fmt.Sprintf(`$(eval "echo \${%s_%s}")`, c.varEvaluationString(name, false), field) +} + func (c *converter) ifStart(condition string, startWord string) error { c.addLine(fmt.Sprintf("%s [ %s -eq %s ]; then", startWord, condition, transpiler.BoolToString(true))) return nil @@ -616,3 +675,11 @@ func (c *converter) nextHelperVar() string { return helperVar } + +func (c *converter) nextDynamicHelperVar() string { + c.addLine(fmt.Sprintf(`_dvc=$((%s+1))`, c.varEvaluationString("_dvc", true))) // Dynamic variable counter. + helper := c.nextHelperVar() + c.VarAssignment(helper, fmt.Sprintf(`_dv%s`, c.varEvaluationString("_dvc", true)), false) + + return helper +} diff --git a/converters/batch/converter.go b/converters/batch/converter.go index 2d11ebc..3abc82a 100644 --- a/converters/batch/converter.go +++ b/converters/batch/converter.go @@ -13,17 +13,18 @@ import ( type helperName = string const ( - appCallHelper helperName = "_ach" // App call - fileReadHelper helperName = "_frh" // File write - fileWriteHelper helperName = "_fwh" // File read - sliceLenSetHelper helperName = "_sls" // Slice length set - sliceLenGetHelper helperName = "_slg" // Slice length get - sliceAssignmentHelper helperName = "_sah" // Slice assignment - sliceCopyHelper helperName = "_sch" // Slice copy - stringSubscriptHelper helperName = "_stsh" // String subscript - stringLengthHelper helperName = "_stlh" // String length - stringEscapeHelper helperName = "_seh" // String escape - echoHelper helperName = "_ech" // Echo + appCallHelper helperName = "_ach" // App call + fileReadHelper helperName = "_frh" // File write + fileWriteHelper helperName = "_fwh" // File read + sliceLenSetHelper helperName = "_sls" // Slice length set + sliceLenGetHelper helperName = "_slg" // Slice length get + sliceAssignmentHelper helperName = "_sah" // Slice assignment + sliceCopyHelper helperName = "_sch" // Slice copy + structAssignmentHelper helperName = "_stah" // Struct assignment + stringSubscriptHelper helperName = "_stsh" // String subscript + stringLengthHelper helperName = "_stlh" // String length + stringEscapeHelper helperName = "_seh" // String escape + echoHelper helperName = "_ech" // Echo ) type funcInfo struct { @@ -39,31 +40,32 @@ type ifInfo struct { } type converter struct { - startCode []string - helperCode []string - globalCode []string - previousFunctionName string - functionsCode [][]string - endCode []string - varCounter int - ifCounter int - forCounter int - endLabels []string - funcs []funcInfo - funcCounter int - fors []forInfo - ifs []ifInfo - lfSet bool - appCallHelperRequired bool - readHelperRequired bool - sliceAssignmentHelperRequired bool - sliceCopyHelperRequired bool - sliceLenSetHelperRequired bool - sliceLenGetHelperRequired bool - stringSubscriptHelperRequired bool - stringLenHelperRequired bool - fileWriteHelperRequired bool - echoHelperRequired bool + startCode []string + helperCode []string + globalCode []string + previousFunctionName string + functionsCode [][]string + endCode []string + varCounter int + ifCounter int + forCounter int + endLabels []string + funcs []funcInfo + funcCounter int + fors []forInfo + ifs []ifInfo + lfSet bool + appCallHelperRequired bool + readHelperRequired bool + sliceAssignmentHelperRequired bool + structAssignmentHelperRequired bool + sliceCopyHelperRequired bool + sliceLenSetHelperRequired bool + sliceLenGetHelperRequired bool + stringSubscriptHelperRequired bool + stringLenHelperRequired bool + fileWriteHelperRequired bool + echoHelperRequired bool } func New() *converter { @@ -184,7 +186,6 @@ func (c *converter) ProgramEnd() error { if c.sliceAssignmentHelperRequired { c.sliceLenGetHelperRequired = true c.sliceLenSetHelperRequired = true - c.sliceAssignmentHelperRequired = true // %1: Slice name // %2: Assigned index @@ -218,6 +219,15 @@ func (c *converter) ProgramEnd() error { ) } + if c.structAssignmentHelperRequired { + // %1: Slice name + // %2: Assigned field + // arg0: Assigned value + c.addHelper("struct assignment", structAssignmentHelper, + c.sliceAssignmentString("!%1!", "%2", fmt.Sprintf("!%s!", funcArgVar(0)), false), + ) + } + if c.stringSubscriptHelperRequired { c.addHelper("string subscript", stringSubscriptHelper, `set /A "_sh=(%2-%1)+1"`, @@ -263,6 +273,12 @@ func (c *converter) SliceAssignment(name string, index string, value string, def return nil } +func (c *converter) StructAssignment(name string, field string, value string, global bool) error { + c.structAssignmentHelperRequired = true + c.callFunc(structAssignmentHelper, []string{value}, c.varName(name, global), field) + return nil +} + func (c *converter) FuncStart(name string, params []string, returnTypes []parser.ValueType) error { c.funcCounter++ c.funcs = append(c.funcs, funcInfo{ @@ -448,8 +464,8 @@ func (c *converter) BinaryOperation(left string, operator parser.BinaryOperator, return notAllowedError() } - switch valueType.DataType() { - case parser.DATA_TYPE_INTEGER: + switch valueType.Type().ElementaryType().Kind() { + case parser.TypeKindInt: switch operator { case parser.BINARY_OPERATOR_MULTIPLICATION, parser.BINARY_OPERATOR_DIVISION, @@ -462,7 +478,7 @@ func (c *converter) BinaryOperation(left string, operator parser.BinaryOperator, return notAllowedError() } c.addLine(fmt.Sprintf(`set /A "%s=%s%s%s"`, c.varName(helper, false), left, operator, right)) - case parser.DATA_TYPE_STRING: + case parser.TypeKindString: switch operator { case parser.BINARY_OPERATOR_ADDITION: c.VarAssignment(helper, fmt.Sprintf("%s%s", left, right), false) @@ -483,15 +499,15 @@ func (c *converter) Comparison(left string, operator parser.CompareOperator, rig quote := "" if !valueType.IsSlice() { - switch valueType.DataType() { - case parser.DATA_TYPE_BOOLEAN: + switch valueType.Type().ElementaryType().Kind() { + case parser.TypeKindBool: switch operator { case parser.COMPARE_OPERATOR_EQUAL: operatorString = EQUAL_OPERATOR case parser.COMPARE_OPERATOR_NOT_EQUAL: operatorString = NOT_EQUAL_OPERATOR } - case parser.DATA_TYPE_INTEGER: + case parser.TypeKindInt: switch operator { case parser.COMPARE_OPERATOR_EQUAL: operatorString = EQUAL_OPERATOR @@ -506,7 +522,7 @@ func (c *converter) Comparison(left string, operator parser.CompareOperator, rig case parser.COMPARE_OPERATOR_LESS_OR_EQUAL: operatorString = "leq" } - case parser.DATA_TYPE_STRING: + case parser.TypeKindString: switch operator { case parser.COMPARE_OPERATOR_EQUAL: operatorString = EQUAL_OPERATOR @@ -579,9 +595,7 @@ func (c *converter) VarEvaluation(name string, valueUsed bool, global bool) (str } func (c *converter) SliceInstantiation(values []string, valueUsed bool) (string, error) { - c.addLine(`set /A "_dvc=!_dvc!+1"`) // Dynamic variable counter. - helper := c.nextHelperVar() - c.VarAssignment(helper, "_dv!_dvc!", false) + helper := c.nextDynamicHelperVar() c.sliceAssignmentHelperRequired = true c.callFunc(sliceLenSetHelper, []string{}, c.varEvaluationString(helper, false), strconv.Itoa(len(values))) @@ -629,6 +643,28 @@ func (c *converter) SliceLen(name string, valueUsed bool) (string, error) { return c.VarEvaluation(helper, valueUsed, false) } +func (c *converter) StructInitialization(values []transpiler.StructValue, valueUsed bool) (string, error) { + helper := c.nextDynamicHelperVar() + + for _, value := range values { + c.addLine(c.structAssignmentString(c.varEvaluationString(helper, false), value.Name(), value.Value(), false)) + } + return c.varEvaluationString(helper, false), nil +} + +func (c *converter) StructEvaluation(name string, field string, valueUsed bool) (string, error) { + helper := c.nextHelperVar() + + c.addLine( + fmt.Sprintf(`for /f "delims=" %%%%i in ("%s_%s") do set "%s=!%%%%i!"`, + c.varEvaluationString(name, false), + field, + c.varName(helper, false), + ), + ) + return c.VarEvaluation(helper, valueUsed, false) +} + func (c *converter) StringSubscript(value string, startIndex string, endIndex string, valueUsed bool) (string, error) { helper := c.nextHelperVar() c.stringSubscriptHelperRequired = true @@ -780,7 +816,11 @@ func (c *converter) varAssignmentString(name string, value string, global bool) } func (c *converter) varEvaluationString(name string, global bool) string { - return fmt.Sprintf("!%s!", c.varName(name, global)) + // Only evaluate if it's not already evaluated. + if !strings.HasPrefix(name, "!") && !strings.HasSuffix(name, "!") { + return fmt.Sprintf("!%s!", c.varName(name, global)) + } + return name } func (c *converter) ifStart(condition string, startAddition string) error { @@ -891,11 +931,22 @@ func (c *converter) nextHelperVar() string { return helperVar } +func (c *converter) nextDynamicHelperVar() string { + c.addLine(`set /A "_dvc=!_dvc!+1"`) // Dynamic variable counter. + helper := c.nextHelperVar() + c.VarAssignment(helper, "_dv!_dvc!", false) + + return helper +} + func (c *converter) sliceAssignmentString(name string, index string, value string, global bool) string { - // TODO: Is global flag even required here? Because value is already passed to function. return fmt.Sprintf(`set "%s_%s=%s"`, name, index, value) } +func (c *converter) structAssignmentString(name string, field string, value string, global bool) string { + return fmt.Sprintf(`set "%s_%s=%s"`, name, field, value) +} + func (c *converter) addLf() { if !c.lfSet { c.addStartLine("(set LF=^") // https://stackoverflow.com/a/60389149 diff --git a/lexer/lexer.go b/lexer/lexer.go index 8812193..3094630 100644 --- a/lexer/lexer.go +++ b/lexer/lexer.go @@ -56,6 +56,7 @@ const ( // Keywords. IMPORT TYPE_DECLARATION + STRUCT CONST_DEFINITION VAR_DEFINITION FUNCTION_DEFINITION @@ -174,6 +175,7 @@ var keywords = map[string]TokenType{ // Common keywords. "import": IMPORT, "type": TYPE_DECLARATION, + "struct": STRUCT, "const": CONST_DEFINITION, "var": VAR_DEFINITION, "func": FUNCTION_DEFINITION, diff --git a/parser/appcall.go b/parser/appcall.go index 1880573..44357f5 100644 --- a/parser/appcall.go +++ b/parser/appcall.go @@ -11,7 +11,7 @@ func (a AppCall) StatementType() StatementType { } func (a AppCall) ValueType() ValueType { - return NewValueType(DATA_TYPE_MULTIPLE, false) + return NewValueType(TypeMultiple{}, false) } func (a AppCall) IsConstant() bool { @@ -32,8 +32,8 @@ func (a AppCall) Next() *AppCall { func (a AppCall) ReturnTypes() []ValueType { return []ValueType{ - NewValueType(DATA_TYPE_STRING, false), // stdout - NewValueType(DATA_TYPE_STRING, false), // stderr - NewValueType(DATA_TYPE_INTEGER, false), // error code + NewValueType(TypeString{}, false), // stdout + NewValueType(TypeString{}, false), // stderr + NewValueType(TypeInt{}, false), // error code } } diff --git a/parser/comparison.go b/parser/comparison.go index 24c02a7..678ab5e 100644 --- a/parser/comparison.go +++ b/parser/comparison.go @@ -19,7 +19,7 @@ func (c Comparison) StatementType() StatementType { } func (c Comparison) ValueType() ValueType { - return ValueType{dataType: DATA_TYPE_BOOLEAN} + return NewValueType(TypeBool{}, false) } func (c Comparison) IsConstant() bool { diff --git a/parser/copy.go b/parser/copy.go index bf13050..66fe847 100644 --- a/parser/copy.go +++ b/parser/copy.go @@ -10,7 +10,7 @@ func (c Copy) StatementType() StatementType { } func (c Copy) ValueType() ValueType { - return NewValueType(DATA_TYPE_INTEGER, false) + return NewValueType(TypeInt{}, false) } func (c Copy) IsConstant() bool { diff --git a/parser/exists.go b/parser/exists.go index 2c1402a..ff4573d 100644 --- a/parser/exists.go +++ b/parser/exists.go @@ -9,7 +9,7 @@ func (e Exists) StatementType() StatementType { } func (e Exists) ValueType() ValueType { - return NewValueType(DATA_TYPE_BOOLEAN, false) + return NewValueType(TypeBool{}, false) } func (e Exists) IsConstant() bool { diff --git a/parser/function.go b/parser/function.go index ed46f3f..0c884c7 100644 --- a/parser/function.go +++ b/parser/function.go @@ -43,6 +43,7 @@ func (e FunctionDefinition) Public() bool { type FunctionCall struct { name string returnTypes []ValueType + params []Variable arguments []Expression } @@ -66,6 +67,10 @@ func (e FunctionCall) ReturnTypes() []ValueType { return e.returnTypes } +func (e FunctionCall) Params() []Variable { + return e.params +} + func (e FunctionCall) Args() []Expression { return e.arguments } @@ -74,7 +79,7 @@ func functionValueType(returnTypes []ValueType) ValueType { var valueType ValueType if len(returnTypes) > 1 { - valueType = NewValueType(DATA_TYPE_MULTIPLE, false) + valueType = NewValueType(TypeMultiple{}, false) } else { valueType = returnTypes[0] } diff --git a/parser/input.go b/parser/input.go index 77be324..73dd0ac 100644 --- a/parser/input.go +++ b/parser/input.go @@ -9,7 +9,7 @@ func (i Input) StatementType() StatementType { } func (i Input) ValueType() ValueType { - return ValueType{dataType: DATA_TYPE_STRING} + return NewValueType(TypeString{}, false) } func (i Input) IsConstant() bool { diff --git a/parser/iota.go b/parser/iota.go index 78d5829..da5f354 100644 --- a/parser/iota.go +++ b/parser/iota.go @@ -8,7 +8,7 @@ func (i Iota) StatementType() StatementType { } func (i Iota) ValueType() ValueType { - return NewValueType(DATA_TYPE_INTEGER, false) + return NewValueType(TypeInt{}, false) } func (i Iota) IsConstant() bool { diff --git a/parser/itoa.go b/parser/itoa.go index 89a30ed..cfe7163 100644 --- a/parser/itoa.go +++ b/parser/itoa.go @@ -9,7 +9,7 @@ func (e Itoa) StatementType() StatementType { } func (e Itoa) ValueType() ValueType { - return NewValueType(DATA_TYPE_STRING, false) + return NewValueType(TypeString{}, false) } func (o Itoa) IsConstant() bool { diff --git a/parser/len.go b/parser/len.go index 067ec1e..3e2d39f 100644 --- a/parser/len.go +++ b/parser/len.go @@ -9,7 +9,7 @@ func (l Len) StatementType() StatementType { } func (l Len) ValueType() ValueType { - return NewValueType(DATA_TYPE_INTEGER, false) + return NewValueType(TypeInt{}, false) } func (l Len) IsConstant() bool { diff --git a/parser/literals.go b/parser/literals.go index 16f6eb8..4b920cb 100644 --- a/parser/literals.go +++ b/parser/literals.go @@ -9,7 +9,7 @@ func (l BooleanLiteral) StatementType() StatementType { } func (l BooleanLiteral) ValueType() ValueType { - return ValueType{dataType: DATA_TYPE_BOOLEAN} + return NewValueType(TypeBool{}, false) } func (l BooleanLiteral) IsConstant() bool { @@ -29,7 +29,7 @@ func (l IntegerLiteral) StatementType() StatementType { } func (l IntegerLiteral) ValueType() ValueType { - return ValueType{dataType: DATA_TYPE_INTEGER} + return NewValueType(TypeInt{}, false) } func (l IntegerLiteral) IsConstant() bool { @@ -44,12 +44,16 @@ type StringLiteral struct { value string } +func NewStringLiteral(value string) StringLiteral { + return StringLiteral{value} +} + func (l StringLiteral) StatementType() StatementType { return STATEMENT_TYPE_STRING_LITERAL } func (l StringLiteral) ValueType() ValueType { - return ValueType{dataType: DATA_TYPE_STRING} + return NewValueType(TypeString{}, false) } func (l StringLiteral) IsConstant() bool { diff --git a/parser/logicaloperation.go b/parser/logicaloperation.go index b1f7357..a28d7e8 100644 --- a/parser/logicaloperation.go +++ b/parser/logicaloperation.go @@ -11,7 +11,7 @@ func (l LogicalOperation) StatementType() StatementType { } func (l LogicalOperation) ValueType() ValueType { - return ValueType{dataType: DATA_TYPE_BOOLEAN} + return NewValueType(TypeBool{}, false) } func (l LogicalOperation) IsConstant() bool { diff --git a/parser/parser.go b/parser/parser.go index 03eab0e..07fcf7a 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -49,9 +49,16 @@ type foundTypeDefinition struct { name string } +type initValue struct { + nameToken lexer.Token + name string + valueToken lexer.Token + value Expression +} + type context struct { imports map[string]string // Maps import aliases to file hashes. - types map[string]typeDefinition // Stores the defined types. + types map[string]Type // Stores the declared types. namedValues map[string][]NamedValue // Stores the variable/constant name to variable/constant relation. functions map[string]FunctionDefinition // Stores the function name to function relation. scopeStack []scope // Stores the current scopes. @@ -62,20 +69,18 @@ type context struct { func newContext() context { c := context{ imports: map[string]string{}, - types: map[string]typeDefinition{}, + types: map[string]Type{}, namedValues: map[string][]NamedValue{}, functions: map[string]FunctionDefinition{}, layer: -1, // Init at -1 since program increases it right away. iotaCounter: 0, } - // Define elementary types. - c.addType(DATA_TYPE_BOOLEAN, NewValueType(DATA_TYPE_BOOLEAN, false), false, true) - c.addType(DATA_TYPE_INTEGER, NewValueType(DATA_TYPE_INTEGER, false), false, true) - c.addType(DATA_TYPE_STRING, NewValueType(DATA_TYPE_STRING, false), false, true) - - // Define error alias. - c.addType(DATA_TYPE_ERROR, NewValueType(DATA_TYPE_STRING, false), true, false) + // Add elementary types. + c.addType(NewValueType(TypeBool{}, false)) + c.addType(NewValueType(TypeInt{}, false)) + c.addType(NewValueType(TypeString{}, false)) + c.addType(NewValueType(TypeError{}, false)) return c } @@ -141,21 +146,19 @@ func (c context) addImport(alias string, hash string) error { return nil } -func (c context) addType(typeName string, valueType ValueType, isAlias bool, isElementary bool) error { - _, exists := c.findType(typeName, false) +func (c context) addType(valueType ValueType) error { + t := valueType.Type() + name := t.Name() + _, exists := c.findType(name) if exists { - return fmt.Errorf("%s has already been defined", typeName) + return fmt.Errorf("type %s has already been defined", name) } if valueType.IsSlice() { // TODO: Add support. - return errors.New("slices are not allowed yet in type definitions") - } - c.types[typeName] = typeDefinition{ - valueType, - isAlias, - isElementary, + return errors.New("slices are not allowed yet in type declarations") } + c.types[name] = t return nil } @@ -193,25 +196,9 @@ func (c context) findImport(alias string) (string, bool) { return hash, exists } -func (c context) findType(typeName string, searchUntilElementary bool) (foundTypeDefinition, bool) { - var foundDefinition foundTypeDefinition - typeDefinition, exists := c.types[typeName] - - if exists { - foundDefinitionTemp := foundTypeDefinition{ - typeDefinition: typeDefinition, - name: typeName, - } - - // If the defined type is an alias, trace it down to the root. - if typeDefinition.isAlias || (searchUntilElementary && !typeDefinition.isElementary) { - foundDefinitionTemp, exists = c.findType(typeDefinition.valueType.DataType(), searchUntilElementary) - } - if exists { - foundDefinition = foundDefinitionTemp - } - } - return foundDefinition, exists +func (c context) findType(typeName string) (Type, bool) { + t, exists := c.types[typeName] + return t, exists } func (c context) findNamedValue(name string, prefix string, global bool) (NamedValue, bool) { @@ -246,7 +233,7 @@ func (c context) clone() context { return context{ imports: maps.Clone(c.imports), types: maps.Clone(c.types), - namedValues: maps.Clone(c.namedValues), + namedValues: maps.Clone(c.namedValues), // TODO: Make sure this is appropriate cloning because each entry contains a slice. functions: maps.Clone(c.functions), scopeStack: slices.Clone(c.scopeStack), layer: c.layer, @@ -355,14 +342,14 @@ func allowedBinaryOperators(t ValueType) []BinaryOperator { operators := []BinaryOperator{} if !t.IsSlice() { - switch t.DataType() { - case DATA_TYPE_INTEGER: + switch t.Type().ElementaryType().Kind() { + case TypeKindInt: operators = []BinaryOperator{BINARY_OPERATOR_MULTIPLICATION, BINARY_OPERATOR_DIVISION, BINARY_OPERATOR_MODULO, BINARY_OPERATOR_ADDITION, BINARY_OPERATOR_SUBTRACTION} - case DATA_TYPE_STRING: + case TypeKindString: operators = []BinaryOperator{BINARY_OPERATOR_ADDITION} default: - // For other types no operations are permitted. } + // For other types no operations are permitted. } return operators } @@ -371,12 +358,12 @@ func allowedCompareOperators(t ValueType) []CompareOperator { operators := []CompareOperator{} if !t.IsSlice() { - switch t.DataType() { - case DATA_TYPE_BOOLEAN: + switch t.Type().ElementaryType().Kind() { + case TypeKindBool: operators = []CompareOperator{COMPARE_OPERATOR_EQUAL, COMPARE_OPERATOR_NOT_EQUAL} - case DATA_TYPE_INTEGER: + case TypeKindInt: operators = []CompareOperator{COMPARE_OPERATOR_EQUAL, COMPARE_OPERATOR_NOT_EQUAL, COMPARE_OPERATOR_LESS, COMPARE_OPERATOR_LESS_OR_EQUAL, COMPARE_OPERATOR_GREATER, COMPARE_OPERATOR_GREATER_OR_EQUAL} - case DATA_TYPE_STRING: + case TypeKindString: operators = []CompareOperator{COMPARE_OPERATOR_EQUAL, COMPARE_OPERATOR_NOT_EQUAL, COMPARE_OPERATOR_LESS, COMPARE_OPERATOR_LESS_OR_EQUAL, COMPARE_OPERATOR_GREATER, COMPARE_OPERATOR_GREATER_OR_EQUAL} default: // For other types no operations are permitted. @@ -386,22 +373,44 @@ func allowedCompareOperators(t ValueType) []CompareOperator { } func defaultVarValue(valueType ValueType, ctx context) (Expression, error) { - foundType, exists := ctx.findType(valueType.DataType(), true) + foundType, exists := ctx.findType(valueType.Type().Name()) if exists { - dataType := foundType.valueType.dataType + elementaryDataType := foundType.ElementaryType() if !valueType.IsSlice() { - switch dataType { - case DATA_TYPE_BOOLEAN: + switch elementaryDataType.Kind() { + case TypeKindBool: return BooleanLiteral{}, nil - case DATA_TYPE_INTEGER: + case TypeKindInt: return IntegerLiteral{}, nil - case DATA_TYPE_STRING: + case TypeKindString: return StringLiteral{}, nil + case TypeKindStruct: + structDefinition, exists := elementaryDataType.(StructDefinition) + + if exists { + structValues := []StructValue{} + + for _, field := range structDefinition.Fields() { + defaultValue, err := defaultVarValue(field.ValueType(), ctx) + + if err != nil { + return nil, err + } + structValues = append(structValues, StructValue{ + StructField: StructField{ + name: field.Name(), + valueType: field.ValueType(), + }, + value: defaultValue, + }) + } + return NewStructInitialization(valueType.Type(), structValues...), nil + } } } else { - return SliceInstantiation{dataType: dataType}, nil + return SliceInstantiation{t: elementaryDataType}, nil } } return nil, fmt.Errorf("no default value found for type %s", valueType.String()) @@ -505,6 +514,10 @@ func (p *Parser) variableNotDefinedError(variable string, token lexer.Token) err return p.notDefinedError("variable", variable, token) } +func (p *Parser) typeNotDefinedError(t string, token lexer.Token) error { + return p.notDefinedError("type", t, token) +} + func (p Parser) peek() lexer.Token { return p.peekAt(0) } @@ -520,6 +533,12 @@ func (p Parser) peekAt(add uint) lexer.Token { return token } +func (p *Parser) skipNewlines() { + for p.peek().Type() == lexer.NEWLINE { + p.eat() + } +} + func (p Parser) findAllowed(searchTokenType lexer.TokenType, allowed ...lexer.TokenType) (lexer.Token, error) { tokens := p.tokens @@ -781,16 +800,10 @@ func (p *Parser) evaluateImports(ctx context) ([]Statement, error) { statementsTemp := []Statement{} for { - var nextToken lexer.Token + // Skip newlines. + p.skipNewlines() - // Skip empty characters. - for { - nextToken = p.peek() - if !slices.Contains([]lexer.TokenType{lexer.NEWLINE}, nextToken.Type()) { - break - } - p.eat() - } + nextToken := p.peek() if nextToken.Type() == lexer.IMPORT { p.eat() @@ -859,7 +872,7 @@ func (p *Parser) evaluateImports(ctx context) ([]Statement, error) { return nil, err } regexp := regexp.MustCompile(`package \w+`) - bodyBytes =regexp.ReplaceAll(bodyBytes, []byte("")) // Remove package statemnt. + bodyBytes = regexp.ReplaceAll(bodyBytes, []byte("")) // Remove package statemnt. err = os.WriteFile(absPath, bodyBytes, 0400) @@ -1181,7 +1194,7 @@ func (p *Parser) evaluateBlock(callback blockCallback, ctx context, scope scope) func (p *Parser) evaluateValueType(ctx context) (ValueType, error) { nextToken := p.peek() - evaluatedType := NewValueType(DATA_TYPE_UNKNOWN, false) + evaluatedType := NewValueType(TypeUnknown{}, false) // Evaluate if value type is a slice type. if nextToken.Type() == lexer.OPENING_SQUARE_BRACKET { @@ -1200,15 +1213,73 @@ func (p *Parser) evaluateValueType(ctx context) (ValueType, error) { return evaluatedType, p.expectedError("data type", nextToken) } p.eat() // Eat data type token. - foundDefinition, exists := ctx.findType(nextToken.Value(), false) + foundDefinition, exists := ctx.findType(nextToken.Value()) if !exists { return evaluatedType, p.expectedError("valid data type", nextToken) } - evaluatedType.dataType = foundDefinition.name + evaluatedType.t = foundDefinition return evaluatedType, nil } +func (p *Parser) evaluateStructDefinition(name string, ctx context) (StructDefinition, error) { + structToken := p.eat() + + if structToken.Type() != lexer.STRUCT { + return StructDefinition{}, p.expectedKeywordError("struct", structToken) + } + nextToken := p.eat() + + if nextToken.Type() != lexer.OPENING_CURLY_BRACKET { + return StructDefinition{}, p.expectedError(`"{"`, nextToken) + } + nextToken = p.eat() + + if nextToken.Type() != lexer.NEWLINE { + return StructDefinition{}, p.expectedNewlineError(nextToken) + } + fields := []StructField{} + + // Evaluate fields. + for { + nameTokens, err := p.evaluateNames() + + if err != nil { + return StructDefinition{}, err + } + valueTypeToken := p.peek() + valueType, err := p.evaluateValueType(ctx) + + if err != nil { + return StructDefinition{}, err + } + + // Don't allow nested structs for now. + if valueType.Type().Kind() == TypeKindStruct { + return StructDefinition{}, p.atError("nested structs are not allowed", valueTypeToken) + } + + for _, nameToken := range nameTokens { + fields = append(fields, StructField{ + name: nameToken.Value(), + valueType: valueType, + }) + } + nextToken = p.eat() + + if nextToken.Type() != lexer.NEWLINE { + return StructDefinition{}, p.expectedNewlineError(nextToken) + } + nextToken = p.peek() + + if nextToken.Type() == lexer.CLOSING_CURLY_BRACKET { + p.eat() // Eat closing curly bracket and break. + break + } + } + return NewStructDefinition(name, fields), nil +} + func (p *Parser) evaluateTypeDeclaration(ctx context) (Statement, error) { typeToken := p.eat() @@ -1221,24 +1292,45 @@ func (p *Parser) evaluateTypeDeclaration(ctx context) (Statement, error) { return nil, p.expectedIdentifierError(nameToken) } isAlias := false + assignToken := p.peek() // If a type is assigned to the new type with an assign-operator, // then it's just an alias. - if p.peek().Type() == lexer.ASSIGN_OPERATOR { + if assignToken.Type() == lexer.ASSIGN_OPERATOR { isAlias = true p.eat() } + name := nameToken.Value() valueTypeToken := p.peek() - valueType, err := p.evaluateValueType(ctx) - if err != nil { - return nil, err + var valueType ValueType + var err error + + if valueTypeToken.Type() == lexer.STRUCT { + if isAlias { + return nil, p.atError("struct alias is not supported", assignToken) + } + structDefinition, err := p.evaluateStructDefinition(name, ctx) + + if err != nil { + return nil, err + } + valueType = NewValueType(structDefinition, false) + } else { + valueType, err = p.evaluateValueType(ctx) + + if err != nil { + return nil, err + } + t := valueType.Type() + + // Create a wrapper with the same type but different name. + valueType = NewValueType(NewTypeCustom(name, isAlias, t.Kind(), t), valueType.IsSlice()) } - name := nameToken.Value() - err = ctx.addType(name, valueType, isAlias, false) + err = ctx.addType(valueType) if err != nil { - return nil, p.atError(err.Error(), valueTypeToken) + return nil, p.atError(err.Error(), nameToken) } return TypeDeclaration{name}, nil } @@ -1313,7 +1405,7 @@ func (p *Parser) evaluateNamedValueDefinition(evalConst bool, ctx context) (Stat return nil, err } } - specifiedType := NewValueType(DATA_TYPE_UNKNOWN, false) + specifiedType := NewValueType(TypeUnknown{}, false) namedValues := []NamedValue{} reuseIota := false @@ -1337,10 +1429,10 @@ func (p *Parser) evaluateNamedValueDefinition(evalConst bool, ctx context) (Stat nextToken = p.peek() } nextTokenType := nextToken.Type() - dataType := specifiedType.DataType() + dataType := specifiedType.Type() // If no data type has been specified and no value is being assigned, return an error. - if dataType == DATA_TYPE_UNKNOWN && nextTokenType != lexer.ASSIGN_OPERATOR { + if dataType.Kind() == TypeKindUnknown && nextTokenType != lexer.ASSIGN_OPERATOR { // If iota has already been used in constant definition and only one value // needs to be assigned, the iota value gets used automatically. if evalConst && useIota && nameTokensLength == 1 { @@ -1372,7 +1464,7 @@ func (p *Parser) evaluateNamedValueDefinition(evalConst bool, ctx context) (Stat variableValueType := namedValue.ValueType() // If the variable already exists, make sure it has the same type as the specified type. - if exists && specifiedType.DataType() != DATA_TYPE_UNKNOWN && !specifiedType.Equals(variableValueType) { + if exists && specifiedType.Type().Kind() != TypeKindUnknown && !specifiedType.Equals(variableValueType) { return nil, p.atError(fmt.Sprintf(`%s %s already exists but has type %s`, noun, name, variableValueType.String()), nextToken) } storedName := name @@ -1454,7 +1546,7 @@ func (p *Parser) evaluateNamedValueDefinition(evalConst bool, ctx context) (Stat } // If a type has been specified, make sure the returned types fit this type. - if specifiedType.DataType() != DATA_TYPE_UNKNOWN { + if specifiedType.Type().Kind() != TypeKindUnknown { for _, valueType := range valuesTypes { if !valueType.Equals(specifiedType) { return nil, p.expectedError(fmt.Sprintf("%s but got %s", specifiedType.String(), valueType.String()), nextToken) @@ -1467,7 +1559,7 @@ func (p *Parser) evaluateNamedValueDefinition(evalConst bool, ctx context) (Stat valueValueType := valuesTypes[i] variableValueType := namedValue.ValueType() - if variableValueType.DataType() == DATA_TYPE_UNKNOWN { + if variableValueType.Type().Kind() == TypeKindUnknown { var updatedNamedValue NamedValue name, layer, public := namedValue.Name(), namedValue.Layer(), namedValue.Public() @@ -1712,7 +1804,7 @@ func (p *Parser) evaluateVarAssignment(ctx context) (Statement, error) { valueType := valuesTypes[i] expectedValueType := namedValue.ValueType() - if valueType != expectedValueType { + if !valueType.Equals(expectedValueType) { return nil, p.expectedError(fmt.Sprintf("%s but got %s", expectedValueType.String(), valueType.String()), valuesToken) } variables = append(variables, NewVariable(name, valueType, namedValue.Layer(), isPublic(name))) @@ -2228,14 +2320,14 @@ func (p *Parser) evaluateFor(ctx context) (Statement, error) { } iterableValueType := iterableExpression.ValueType() layer := ctx.layer + 1 - indexVar := NewVariable(indexVarName, NewValueType(DATA_TYPE_INTEGER, false), layer, false) + indexVar := NewVariable(indexVarName, NewValueType(TypeInt{}, false), layer, false) var iterableEvaluation Expression if iterableValueType.IsSlice() { iterableEvaluation = SliceEvaluation{ - value: iterableExpression, - index: VariableEvaluation{indexVar}, - dataType: iterableValueType.DataType(), + value: iterableExpression, + index: VariableEvaluation{indexVar}, + t: iterableValueType.Type(), } } else if iterableValueType.IsString() { iterableEvaluation = StringSubscript{ @@ -2425,10 +2517,10 @@ func (p *Parser) evaluateTypeDefinition(ctx context) (Expression, error) { return nil, p.expectedIdentifierError(identifierToken) } typeName := identifierToken.Value() - foundElementaryDefinition, exists := ctx.findType(typeName, true) + definitionTypeDeclaration, exists := ctx.findType(typeName) if !exists { - return nil, p.notDefinedError("type", typeName, identifierToken) + return nil, p.typeNotDefinedError(typeName, identifierToken) } nextToken := p.eat() @@ -2442,14 +2534,15 @@ func (p *Parser) evaluateTypeDefinition(ctx context) (Expression, error) { return nil, err } exprValueType := expr.ValueType() - exprBaseTypeDefinition, _ := ctx.findType(exprValueType.DataType(), true) - exprBaseValueType := exprBaseTypeDefinition.valueType - foundElementaryDefinitionValueType := foundElementaryDefinition.valueType - baseDefinition, _ := ctx.findType(typeName, false) - baseTypeName := baseDefinition.name - if !foundElementaryDefinitionValueType.Equals(exprBaseValueType) { - return nil, p.atError(fmt.Sprintf(`%s cannot be converted into %s`, exprValueType.String(), baseTypeName), nextToken) + if !exists { + return nil, p.typeNotDefinedError(typeName, identifierToken) + } + definitionAliasType := definitionTypeDeclaration.ElementaryType() + exprAliasType := exprValueType.Type().ElementaryType() + + if !definitionAliasType.Equals(exprAliasType) { + return nil, p.atError(fmt.Sprintf(`%s cannot be converted into %s`, exprValueType.String(), definitionTypeDeclaration.Name()), nextToken) } nextToken = p.eat() @@ -2459,7 +2552,50 @@ func (p *Parser) evaluateTypeDefinition(ctx context) (Expression, error) { return TypeDefinition{ value: expr, - valueType: NewValueType(baseTypeName, exprValueType.IsSlice()), + valueType: NewValueType(definitionTypeDeclaration, exprValueType.IsSlice()), + }, nil +} + +func (p *Parser) evaluateStructEvaluation(importAlias string, ctx context) (Expression, error) { + identifierToken := p.eat() // Eat identifier token. + + if identifierToken.Type() != lexer.IDENTIFIER { + return nil, p.expectedIdentifierError(identifierToken) + } + name := identifierToken.Value() + namedValue, exists := ctx.findNamedValue(name, p.prefix, ctx.global()) + + if !exists { + return nil, p.variableNotDefinedError(name, identifierToken) + } + dotToken := p.eat() + + if dotToken.Type() != lexer.DOT { + return nil, p.expectedError(`"."`, dotToken) + } + fieldToken := p.eat() + + if fieldToken.Type() != lexer.IDENTIFIER { + return nil, p.expectedError("field name", fieldToken) + } + typeDeclaration := namedValue.ValueType().Type() + typeDeclarationKind := typeDeclaration.Kind() + + if typeDeclarationKind != TypeKindStruct { + return nil, p.expectedError(fmt.Sprintf("%s but got %s", TypeKindStruct, typeDeclarationKind), identifierToken) + } + structDefinition := typeDeclaration.(StructDefinition) + + // Check field. + fieldName := fieldToken.Value() + foundField, err := structDefinition.FindField(fieldName) + + if err != nil { + return nil, p.atError(err.Error(), fieldToken) + } + return StructEvaluation{ + Variable: namedValue.(Variable), + field: foundField, }, nil } @@ -2486,6 +2622,27 @@ func (p *Parser) evaluateNamedValueEvaluation(ctx context) (Expression, error) { }, nil } +func (p *Parser) evaluateImportAlias(ctx context) (string, lexer.Token, error) { + nextToken := p.peek() + + // If next token is an identifier, try to find an import for it. + if nextToken.Type() == lexer.IDENTIFIER { + alias := nextToken.Value() + _, exists := ctx.findImport(alias) + + if exists { + p.eat() + nextToken = p.eat() // Eat dot token. + + if nextToken.Type() != lexer.DOT { + return "", nextToken, p.expectedError(`"."`, nextToken) + } + return alias, p.peek(), nil + } + } + return "", nextToken, nil +} + func (p *Parser) evaluateSingleExpression(ctx context) (Expression, error) { var err error var expr Expression @@ -2546,7 +2703,7 @@ func (p *Parser) evaluateSingleExpression(ctx context) (Expression, error) { // Handle slice instantiation. case lexer.OPENING_SQUARE_BRACKET: - expr, err = p.evaluateSliceInstantiation(ctx) + expr, err = p.evaluateSliceInitialization(ctx) // Handle iota. case lexer.IOTA: @@ -2582,25 +2739,38 @@ func (p *Parser) evaluateSingleExpression(ctx context) (Expression, error) { // Handle identifiers. case lexer.IDENTIFIER: + importAlias, _, errTemp := p.evaluateImportAlias(ctx) + err = errTemp + + if err != nil { + return nil, err + } nextToken := p.peekAt(1) - // If the current token is an identifier and the next is an opening - // round bracket or a dot, it's a function call if the next is an - // opening square bracket, it's a slice evaluation, otherwise it's - // a variable evaluation. switch nextToken.Type() { - case lexer.OPENING_ROUND_BRACKET, lexer.DOT: + case lexer.OPENING_ROUND_BRACKET: // If a type exists with the provided name, it's a type-cast/-instantiation. - _, exists := ctx.findType(value, false) + _, exists := ctx.findType(value) if exists { expr, err = p.evaluateTypeDefinition(ctx) } else { - expr, err = p.evaluateFunctionCall(ctx) + expr, err = p.evaluateFunctionCall(importAlias, ctx) } case lexer.OPENING_SQUARE_BRACKET: expr, err = p.evaluateSubscript(ctx) - default: + case lexer.OPENING_CURLY_BRACKET: + _, valid := ctx.findType(value) + + if valid { + expr, err = p.evaluateStructInitialization(ctx) + } + case lexer.DOT: + expr, err = p.evaluateStructEvaluation(importAlias, ctx) + } + + // If nothing has been set yet, try to evaluate named value. + if expr == nil && err == nil { expr, err = p.evaluateNamedValueEvaluation(ctx) } @@ -2713,7 +2883,7 @@ func (p *Parser) evaluateStatement(ctx context) (Statement, error) { } else { // If token is identifier it could be a slice assignment, an increment or a decrement. if token.Type() == lexer.IDENTIFIER { - switch p.peekAt(1).Type() { + switch nextTokenType := p.peekAt(1).Type(); nextTokenType { case lexer.INCREMENT_OPERATOR, lexer.DECREMENT_OPERATOR: stmt, err = p.evaluateIncrementDecrement(ctx) case lexer.COMPOUND_ASSIGN_OPERATOR: @@ -2721,12 +2891,18 @@ func (p *Parser) evaluateStatement(ctx context) (Statement, error) { case lexer.ASSIGN_OPERATOR, lexer.COMMA: stmt, err = p.evaluateVarAssignment(ctx) default: - // Handle slice assignment. variable, exists := ctx.findNamedValue(token.Value(), p.prefix, ctx.global()) - // If variable has been defined and is a slice, handles slice assignment. - if exists && variable.ValueType().IsSlice() { - stmt, err = p.evaluateSliceAssignment(ctx) + switch nextTokenType { + case lexer.DOT: + // Could be a library variable or a struct assignment. + // TODO: Handle library stuff as well, but for now handle struct assignment. + stmt, err = p.evaluateStructAssignment(ctx) + default: + // If variable has been defined and is a slice, handles slice assignment. + if exists && variable.ValueType().IsSlice() { + stmt, err = p.evaluateSliceAssignment(ctx) + } } } } @@ -2811,7 +2987,7 @@ func (p *Parser) evaluateComparison(ctx context) (Expression, error) { rightType := rightExpression.ValueType() if !leftType.Equals(rightType) { - return nil, p.expectedError(fmt.Sprintf("same comparison types but got %s and %s", leftType.DataType(), rightType.String()), operatorToken) + return nil, p.expectedError(fmt.Sprintf("same comparison types but got %s and %s", leftType.String(), rightType.String()), operatorToken) } allowedOperators := allowedCompareOperators(leftType) @@ -2902,7 +3078,7 @@ func (p *Parser) evaluateArguments(typeName string, name string, params []Variab lastArgType := expr.ValueType() if !lastParamType.Equals(lastArgType) { - return nil, p.expectedError(fmt.Sprintf("parameter of type %s (%s) but got %s", lastParamType.String(), param.Name(), lastArgType.String()), argToken) + return nil, p.expectedError(fmt.Sprintf("type of parameter %s is %s but got %s", param.Name(), lastParamType.String(), lastArgType.String()), argToken) } } nextToken = p.peek() @@ -2937,17 +3113,8 @@ func (p *Parser) evaluateArguments(typeName string, name string, params []Variab return args, nil } -func (p *Parser) evaluateFunctionCall(ctx context) (Call, error) { +func (p *Parser) evaluateFunctionCall(importAlias string, ctx context) (Call, error) { nextToken := p.eat() - dotToken := p.peek() - alias := "" - - // If next token is a dot, it's an include-function call. - if dotToken.Type() == lexer.DOT { - p.eat() - alias = nextToken.Value() - nextToken = p.eat() - } if nextToken.Type() != lexer.IDENTIFIER { return nil, p.expectedError("function identifier", nextToken) @@ -2957,9 +3124,9 @@ func (p *Parser) evaluateFunctionCall(ctx context) (Call, error) { dotedName := name // If it's an include-function call, use provided alias. - if len(alias) > 0 { - prefix = alias - dotedName = fmt.Sprintf("%s.%s", alias, name) + if len(importAlias) > 0 { + prefix = importAlias + dotedName = fmt.Sprintf("%s.%s", importAlias, name) } // Make sure function has been defined. @@ -2987,6 +3154,7 @@ func (p *Parser) evaluateFunctionCall(ctx context) (Call, error) { return FunctionCall{ name: name, arguments: args, + params: definedFunction.Params(), returnTypes: definedFunction.ReturnTypes(), }, nil } @@ -3029,50 +3197,68 @@ func (p *Parser) evaluateAppCall(ctx context) (Call, error) { return call, nil } -func (p *Parser) evaluateSliceInstantiation(ctx context) (Expression, error) { - nextToken := p.peek() - sliceValueType, err := p.evaluateValueType(ctx) - - if err != nil { - return nil, err - } - if !sliceValueType.IsSlice() { - return nil, p.expectedError(fmt.Sprintf("slice type but got %s", sliceValueType.String()), nextToken) - } - nextToken = p.eat() +func (p *Parser) evaluateInitializationValues(checkCallout func(initValue initValue) error, ctx context) ([]initValue, error) { + nextToken := p.eat() if nextToken.Type() != lexer.OPENING_CURLY_BRACKET { return nil, p.expectedError(`"{"`, nextToken) } nextToken = p.peek() - values := []Expression{} + names := []string{} + initValues := []initValue{} + rowInit := p.peek().Type() == lexer.NEWLINE // If there's a newline after the bracket, it's a row initialization. - // Evaluate slice initialization values. + // Evaluate initialization values. if nextToken.Type() != lexer.CLOSING_CURLY_BRACKET { for { + // Get rid of consecutive newlines. + p.skipNewlines() + + nameToken := p.peek() + initValue := initValue{nameToken: nameToken} + + // Check if a name is defined. + if nameToken.Type() == lexer.IDENTIFIER && p.peekAt(1).Type() == lexer.COLON { + p.eat() // Eat name token. + p.eat() // Eat colon token. + + name := nameToken.Value() + + if slices.Contains(names, name) { + return nil, p.atError(fmt.Sprintf("a value has already been assigned to %s", name), nameToken) + } + names = append(names, name) + initValue.name = name + } valueToken := p.peek() expr, err := p.evaluateExpression(ctx) if err != nil { return nil, err } - valueDataType := expr.ValueType() - sliceElementValueType := sliceValueType - sliceElementValueType.isSlice = false + initValue.valueToken = valueToken + initValue.value = expr + + err = checkCallout(initValue) - if !valueDataType.Equals(sliceElementValueType) { - return nil, p.atError(fmt.Sprintf("%s cannot not be added to %s", valueDataType.String(), sliceElementValueType.String()), valueToken) + if err != nil { + return nil, err } - values = append(values, expr) + initValues = append(initValues, initValue) nextToken = p.peek() nextTokenType := nextToken.Type() + if rowInit && nextTokenType != lexer.COMMA { + return nil, p.expectedError(`","`, nextToken) + } + if nextTokenType == lexer.COMMA { p.eat() - } else if nextTokenType == lexer.CLOSING_CURLY_BRACKET { + } + p.skipNewlines() + + if p.peek().Type() == lexer.CLOSING_CURLY_BRACKET { break - } else { - return nil, p.expectedError(`"," or "}"`, nextToken) } } } @@ -3081,10 +3267,93 @@ func (p *Parser) evaluateSliceInstantiation(ctx context) (Expression, error) { if nextToken.Type() != lexer.CLOSING_CURLY_BRACKET { return nil, p.expectedError(`"}"`, nextToken) } - return SliceInstantiation{ - dataType: sliceValueType.DataType(), - values: values, - }, nil + return initValues, nil +} + +func (p *Parser) evaluateSliceInitialization(ctx context) (Expression, error) { + nextToken := p.peek() + sliceValueType, err := p.evaluateValueType(ctx) + + if err != nil { + return nil, err + } + + if !sliceValueType.IsSlice() { + return nil, p.expectedError(fmt.Sprintf("slice type but got %s", sliceValueType.String()), nextToken) + } + initValues, err := p.evaluateInitializationValues(func(initValue initValue) error { + if len(initValue.name) > 0 { + return p.atError("unexpected name", initValue.nameToken) + } + valueDataType := initValue.value.ValueType() + sliceElementValueType := sliceValueType + sliceElementValueType.isSlice = false + + if !valueDataType.Equals(sliceElementValueType) { + return p.atError(fmt.Sprintf("%s cannot not be added to %s", valueDataType.String(), sliceElementValueType.String()), initValue.valueToken) + } + return nil + }, ctx) + + if err != nil { + return nil, err + } + values := []Expression{} + + for _, initValue := range initValues { + values = append(values, initValue.value) + } + initialization := SliceInstantiation{ + t: sliceValueType.Type(), + values: values, + } + return initialization, nil +} + +func (p *Parser) evaluateStructInitialization(ctx context) (Expression, error) { + nextToken := p.peek() + structValueType, err := p.evaluateValueType(ctx) + + if err != nil { + return nil, err + } + intialization, err := defaultVarValue(structValueType, ctx) + + if err != nil { + return nil, err + } + structInitialization := intialization.(StructInitialization) + structDefinition, valid := structValueType.Type().(StructDefinition) + + if !valid || structValueType.IsSlice() { + return nil, p.expectedError(fmt.Sprintf("struct type but got %s", structValueType.String()), nextToken) + } + _, err = p.evaluateInitializationValues(func(initValue initValue) error { + fieldName := initValue.name + + if len(fieldName) == 0 { + return p.expectedError("field name", initValue.nameToken) + } + structField, err := structDefinition.FindField(fieldName) + + if err != nil { + return err + } + value := initValue.value + valueDataType := value.ValueType() + structFieldValueType := structField.ValueType() + + if !valueDataType.Equals(structFieldValueType) { + return p.expectedError(fmt.Sprintf("%s value but got %s", structFieldValueType.String(), valueDataType.String()), initValue.valueToken) + } + structInitialization.values = append(structInitialization.values, NewStructValue(fieldName, structFieldValueType, value)) + return nil + }, ctx) + + if err != nil { + return nil, err + } + return structInitialization, nil } func (p *Parser) evaluateSubscript(ctx context) (Expression, error) { @@ -3108,7 +3377,7 @@ func (p *Parser) evaluateSubscript(ctx context) (Expression, error) { valueType := value.ValueType() isSlice := valueType.IsSlice() - if !isSlice && valueType.DataType() != DATA_TYPE_STRING { + if !isSlice && valueType.Type().Kind() != TypeKindString { return nil, p.expectedError("slice or string", valueToken) } nextToken := p.eat() @@ -3135,7 +3404,7 @@ func (p *Parser) evaluateSubscript(ctx context) (Expression, error) { startIndexValueType := startIndex.ValueType() if !startIndexValueType.IsInt() { - return nil, p.expectedError(fmt.Sprintf("%s as start-index but got %s", DATA_TYPE_INTEGER, startIndexValueType.String()), startToken) + return nil, p.expectedError(fmt.Sprintf("%s as start-index but got %s", TypeKindInt, startIndexValueType.String()), startToken) } nextToken = p.peek() @@ -3186,7 +3455,7 @@ func (p *Parser) evaluateSubscript(ctx context) (Expression, error) { endIndexValueType := endIndex.ValueType() if !endIndexValueType.IsInt() { - return nil, p.expectedError(fmt.Sprintf("%s as stop-index but got %s", DATA_TYPE_INTEGER, endIndexValueType.String()), endToken) + return nil, p.expectedError(fmt.Sprintf("%s as stop-index but got %s", TypeKindInt, endIndexValueType.String()), endToken) } if !isSlice { @@ -3197,9 +3466,9 @@ func (p *Parser) evaluateSubscript(ctx context) (Expression, error) { }, nil } return SliceEvaluation{ - value: value, - index: startIndex, - dataType: valueType.DataType(), + value: value, + index: startIndex, + t: valueType.Type(), }, nil } @@ -3236,7 +3505,7 @@ func (p *Parser) evaluateSliceAssignment(ctx context) (Statement, error) { indexValueType := index.ValueType() if !indexValueType.IsInt() { - return nil, p.expectedError(fmt.Sprintf("%s as index but got %s", DATA_TYPE_INTEGER, indexValueType.String()), nextToken) + return nil, p.expectedError(fmt.Sprintf("%s as index but got %s", TypeKindInt, indexValueType.String()), nextToken) } nextToken = p.eat() @@ -3254,11 +3523,11 @@ func (p *Parser) evaluateSliceAssignment(ctx context) (Statement, error) { if err != nil { return nil, err } - variableDataType := variableValueType.DataType() - assignedDataType := value.ValueType().DataType() + variableType := variableValueType.Type() + assignedType := value.ValueType().Type() - if variableDataType != assignedDataType { - return nil, p.expectedError(fmt.Sprintf("%s value but got %s", variableDataType, assignedDataType), valueToken) + if !variableType.Equals(assignedType) { + return nil, p.expectedError(fmt.Sprintf("%s value but got %s", variableType.Name(), assignedType.Name()), valueToken) } return SliceAssignment{ Variable: namedValue.(Variable), @@ -3267,6 +3536,70 @@ func (p *Parser) evaluateSliceAssignment(ctx context) (Statement, error) { }, nil } +func (p *Parser) evaluateStructAssignment(ctx context) (Statement, error) { + nameToken := p.eat() + + if nameToken.Type() != lexer.IDENTIFIER { + return nil, p.expectedError("struct variable", nameToken) + } + name := nameToken.Value() + namedValue, exists := ctx.findNamedValue(name, p.prefix, ctx.global()) + + if !exists { + return nil, p.variableNotDefinedError(name, nameToken) + } else if namedValue.IsConstant() { + return nil, p.constantError(name, nameToken) + } + namedValueValueType := namedValue.ValueType() + namedValueBaseType := namedValueValueType.Type() + + if namedValueValueType.IsSlice() { + return nil, p.expectedError("struct but got slice", nameToken) + } else if namedValueBaseType.Kind() != TypeKindStruct { + return nil, p.expectedError(fmt.Sprintf("struct but variable is of type %s", namedValueBaseType.Kind()), nameToken) + } + structDefinition := namedValueBaseType.(StructDefinition) + nextToken := p.eat() + + if nextToken.Type() != lexer.DOT { + return nil, p.expectedError(`"."`, nextToken) + } + nextToken = p.eat() + + if nextToken.Type() != lexer.IDENTIFIER { + return nil, p.expectedError(`struct field`, nextToken) + } + structField, err := structDefinition.FindField(nextToken.Value()) + + if err != nil { + return nil, p.atError(err.Error(), nextToken) + } + nextToken = p.eat() + + if nextToken.Type() != lexer.ASSIGN_OPERATOR { + return nil, p.expectedError(`"="`, nameToken) + } + valueToken := p.peek() + value, err := p.evaluateExpression(ctx) + + if err != nil { + return nil, err + } + variableValueType := structField.ValueType() + assignedValueType := value.ValueType() + + if !variableValueType.Equals(assignedValueType) { + return nil, p.expectedError(fmt.Sprintf("%s value but got %s", variableValueType.String(), assignedValueType.String()), valueToken) + } + return StructAssignment{ + Variable: namedValue.(Variable), + value: StructValue{ + StructField: structField, + value: value, + }, + }, nil +} + func (p *Parser) evaluateIncrementDecrement(ctx context) (Statement, error) { identifierToken := p.eat() @@ -3285,7 +3618,7 @@ func (p *Parser) evaluateIncrementDecrement(ctx context) (Statement, error) { valueType := variable.ValueType() if !valueType.IsInt() { - return nil, p.expectedError(fmt.Sprintf("%s but got %s", NewValueType(DATA_TYPE_INTEGER, false).String(), valueType.String()), identifierToken) + return nil, p.expectedError(fmt.Sprintf("%s but got %s", NewValueType(TypeInt{}, false).String(), valueType.String()), identifierToken) } operationToken := p.eat() increment := true diff --git a/parser/read.go b/parser/read.go index 7e65730..4162a43 100644 --- a/parser/read.go +++ b/parser/read.go @@ -9,7 +9,7 @@ func (r Read) StatementType() StatementType { } func (r Read) ValueType() ValueType { - return NewValueType(DATA_TYPE_STRING, false) + return NewValueType(TypeString{}, false) } func (r Read) IsConstant() bool { diff --git a/parser/slice.go b/parser/slice.go index 199ce9f..69cf930 100644 --- a/parser/slice.go +++ b/parser/slice.go @@ -1,8 +1,8 @@ package parser type SliceInstantiation struct { - dataType DataType - values []Expression + t Type + values []Expression } func (s SliceInstantiation) StatementType() StatementType { @@ -10,7 +10,7 @@ func (s SliceInstantiation) StatementType() StatementType { } func (s SliceInstantiation) ValueType() ValueType { - return ValueType{dataType: s.dataType, isSlice: true} + return ValueType{t: s.t, isSlice: true} } func (s SliceInstantiation) IsConstant() bool { @@ -22,9 +22,9 @@ func (s SliceInstantiation) Values() []Expression { } type SliceEvaluation struct { - value Expression - index Expression - dataType DataType + value Expression + index Expression + t Type } func (s SliceEvaluation) StatementType() StatementType { @@ -40,7 +40,7 @@ func (s SliceEvaluation) Index() Expression { } func (s SliceEvaluation) ValueType() ValueType { - return ValueType{dataType: s.dataType} + return ValueType{t: s.t} } func (s SliceEvaluation) IsConstant() bool { diff --git a/parser/string.go b/parser/string.go index ef49e8c..7ceabfc 100644 --- a/parser/string.go +++ b/parser/string.go @@ -11,7 +11,7 @@ func (s StringSubscript) StatementType() StatementType { } func (s StringSubscript) ValueType() ValueType { - return NewValueType(DATA_TYPE_STRING, false) + return NewValueType(TypeString{}, false) } func (s StringSubscript) IsConstant() bool { diff --git a/parser/struct.go b/parser/struct.go new file mode 100644 index 0000000..c4aaa9a --- /dev/null +++ b/parser/struct.go @@ -0,0 +1,156 @@ +package parser + +import "fmt" + +type StructField struct { + name string + valueType ValueType +} + +func (f StructField) Name() string { + return f.name +} + +func (f StructField) ValueType() ValueType { + return f.valueType +} + +type StructDefinition struct { + name string + fields []StructField +} + +func NewStructDefinition(name string, fields []StructField) StructDefinition { + return StructDefinition{ + name, + fields, + } +} + +func (d StructDefinition) Fields() []StructField { + return d.fields +} + +func (d StructDefinition) Name() string { + return d.name +} + +func (d StructDefinition) IsAlias() bool { + return false +} + +func (d StructDefinition) Kind() TypeKind { + return TypeKindStruct +} + +func (d StructDefinition) Base() Type { + return nil +} + +func (d StructDefinition) Equals(c Type) bool { + compareType, isDeclaration := c.(StructDefinition) + + if !isDeclaration { + return false + } + fieldsD1 := d.Fields() + fieldsD2 := compareType.Fields() + + if len(fieldsD1) != len(fieldsD2) { + return false + } + + for i, fieldD1 := range fieldsD1 { + fieldD2 := fieldsD2[i] + + if fieldD1.Name() != fieldD2.Name() || !fieldD1.ValueType().Equals(fieldD2.ValueType()) { + return false + } + } + return true +} + +func (t StructDefinition) ElementaryType() Type { return elementaryType(t) } +func (t StructDefinition) AliasedType() Type { return aliasedType(t) } + +func (d StructDefinition) FindField(name string) (StructField, error) { + for _, field := range d.Fields() { + if field.Name() == name { + return field, nil + } + } + return StructField{}, fmt.Errorf(`struct field %s doesn't exist`, name) +} + +type StructValue struct { + StructField + value Expression +} + +func NewStructValue(name string, valueType ValueType, value Expression) StructValue { + return StructValue{ + StructField: StructField{ + name, + valueType, + }, + value: value, + } +} + +func (v StructValue) Value() Expression { + return v.value +} + +type StructInitialization struct { + t Type + values []StructValue +} + +func NewStructInitialization(t Type, values ...StructValue) StructInitialization { + return StructInitialization{ + t, + values, + } +} + +func (d StructInitialization) StatementType() StatementType { + return STATEMENT_TYPE_STRUCT_DEFINITION +} + +func (d StructInitialization) ValueType() ValueType { + return NewValueType(d.t, false) +} + +func (d StructInitialization) IsConstant() bool { + return false +} + +func (d StructInitialization) Values() []StructValue { + return d.values +} + +type StructAssignment struct { + Variable + value StructValue +} + +func (a StructAssignment) StatementType() StatementType { + return STATEMENT_TYPE_STRUCT_ASSIGNMENT +} + +func (a StructAssignment) Value() StructValue { + return a.value +} + +type StructEvaluation struct { + Variable + field StructField +} + +func (e StructEvaluation) StatementType() StatementType { + return STATEMENT_TYPE_STRUCT_EVALUATION +} + +func (e StructEvaluation) Field() StructField { + return e.field +} diff --git a/parser/types.go b/parser/types.go index f9754d4..293d6bb 100644 --- a/parser/types.go +++ b/parser/types.go @@ -6,26 +6,179 @@ import ( type StatementType string type AssignmentType string -type DataType = string +type TypeKind string type CompareOperator = string type UnaryOperator = string type BinaryOperator = string type LogicalOperator = string +const ( + TypeKindUnknown TypeKind = "unknown" + TypeKindBool TypeKind = "bool" + TypeKindInt TypeKind = "int" + TypeKindString TypeKind = "string" + TypeKindError TypeKind = "error" + TypeKindStruct TypeKind = "struct" + TypeKindMultiple TypeKind = "multiple" +) + +type Type interface { + Name() string + IsAlias() bool + Kind() TypeKind + Base() Type + Equals(compareType Type) bool + ElementaryType() Type + AliasedType() Type +} + +func equals(t1 Type, t2 Type) bool { + base1 := t1.AliasedType() + base2 := t2.AliasedType() + + return base1.Name() == base2.Name() && base1.Kind() == base2.Kind() +} + +func elementaryType(t Type) Type { + base := t.Base() + + if base != nil { + return base.ElementaryType() + } + return t +} + +func aliasedType(t Type) Type { + base := t.Base() + + if t.IsAlias() && base != nil { + return base.AliasedType() + } + return t +} + +type TypeUnknown struct{} + +func (t TypeUnknown) Name() string { return "unknown" } +func (t TypeUnknown) IsAlias() bool { return false } +func (t TypeUnknown) Kind() TypeKind { return TypeKindUnknown } +func (t TypeUnknown) Base() Type { return nil } +func (t TypeUnknown) Equals(compareType Type) bool { return equals(t, compareType) } +func (t TypeUnknown) ElementaryType() Type { return elementaryType(t) } +func (t TypeUnknown) AliasedType() Type { return aliasedType(t) } + +type TypeCustom struct { + name string + isAlias bool + kind TypeKind + base Type +} + +func NewTypeCustom(name string, isAlias bool, kind TypeKind, base Type) TypeCustom { + return TypeCustom{ + name, + isAlias, + kind, + base, + } +} + +func (t TypeCustom) Name() string { return t.name } +func (t TypeCustom) IsAlias() bool { return t.isAlias } +func (t TypeCustom) Kind() TypeKind { return t.kind } +func (t TypeCustom) Base() Type { return t.base } +func (t TypeCustom) Equals(compareType Type) bool { return equals(t, compareType) } +func (t TypeCustom) ElementaryType() Type { return elementaryType(t) } +func (t TypeCustom) AliasedType() Type { return aliasedType(t) } + +type TypeBool struct{} + +func (t TypeBool) Name() string { return "bool" } +func (t TypeBool) IsAlias() bool { return false } +func (t TypeBool) Kind() TypeKind { return TypeKindBool } +func (t TypeBool) Base() Type { return nil } +func (t TypeBool) Equals(compareType Type) bool { return equals(t, compareType) } +func (t TypeBool) ElementaryType() Type { return elementaryType(t) } +func (t TypeBool) AliasedType() Type { return aliasedType(t) } + +type TypeInt struct{} + +func (t TypeInt) Name() string { return "int" } +func (t TypeInt) IsAlias() bool { return false } +func (t TypeInt) Kind() TypeKind { return TypeKindInt } +func (t TypeInt) Base() Type { return nil } +func (t TypeInt) Equals(compareType Type) bool { return equals(t, compareType) } +func (t TypeInt) ElementaryType() Type { return elementaryType(t) } +func (t TypeInt) AliasedType() Type { return aliasedType(t) } + +type TypeString struct{} + +func (t TypeString) Name() string { return "string" } +func (t TypeString) IsAlias() bool { return false } +func (t TypeString) Kind() TypeKind { return TypeKindString } +func (t TypeString) Base() Type { return nil } +func (t TypeString) Equals(compareType Type) bool { return equals(t, compareType) } +func (t TypeString) ElementaryType() Type { return elementaryType(t) } +func (t TypeString) AliasedType() Type { return aliasedType(t) } + +type TypeError struct{} + +func (t TypeError) Name() string { return "error" } +func (t TypeError) IsAlias() bool { return true } +func (t TypeError) Kind() TypeKind { return TypeKindError } +func (t TypeError) Base() Type { return TypeString{} } +func (t TypeError) Equals(compareType Type) bool { return equals(t, compareType) } +func (t TypeError) ElementaryType() Type { return elementaryType(t) } +func (t TypeError) AliasedType() Type { return aliasedType(t) } + +type TypeMultiple struct { + types []Type +} + +func (t TypeMultiple) Name() string { return "multiple" } +func (t TypeMultiple) IsAlias() bool { return false } +func (t TypeMultiple) Kind() TypeKind { return TypeKindMultiple } +func (t TypeMultiple) Base() Type { return nil } +func (t TypeMultiple) Equals(c Type) bool { + ct, isType := c.(TypeMultiple) + + if isType { + typesT1 := t.Types() + typesT2 := ct.Types() + + if len(typesT1) != len(typesT2) { + return false + } + + for i, t1 := range typesT1 { + if !t1.Equals(typesT2[i]) { + return false + } + } + } + return false +} +func (t TypeMultiple) ElementaryType() Type { return elementaryType(t) } +func (t TypeMultiple) AliasedType() Type { return aliasedType(t) } + +func (t TypeMultiple) Types() []Type { + return t.types +} + type ValueType struct { - dataType DataType - isSlice bool + t Type + isSlice bool } -func NewValueType(dataType DataType, isSlice bool) ValueType { +func NewValueType(t Type, isSlice bool) ValueType { return ValueType{ - dataType, + t, isSlice, } } -func (vt ValueType) DataType() DataType { - return vt.dataType +func (vt ValueType) Type() Type { + return vt.t } func (vt ValueType) IsSlice() bool { @@ -33,7 +186,7 @@ func (vt ValueType) IsSlice() bool { } func (vt ValueType) String() string { - s := string(vt.dataType) + s := string(vt.Type().Name()) if vt.isSlice { s = fmt.Sprintf("[]%s", s) @@ -42,29 +195,34 @@ func (vt ValueType) String() string { } func (vt ValueType) Equals(valueType ValueType) bool { - return vt.DataType() == valueType.DataType() && vt.IsSlice() == valueType.IsSlice() + return vt.Type().Equals(valueType.Type()) && vt.IsSlice() == valueType.IsSlice() } func (vt ValueType) IsBool() bool { - return vt.isNonSliceType(DATA_TYPE_BOOLEAN) + return vt.isNonSliceType(TypeBool{}) } func (vt ValueType) IsInt() bool { - return vt.isNonSliceType(DATA_TYPE_INTEGER) + return vt.isNonSliceType(TypeInt{}) } func (vt ValueType) IsString() bool { - return vt.isNonSliceType(DATA_TYPE_STRING) + return vt.isNonSliceType(TypeString{}) } -func (vt ValueType) isNonSliceType(dataType DataType) bool { - return vt.DataType() == dataType && !vt.IsSlice() +func (vt ValueType) isNonSliceType(t Type) bool { + return vt.Type().Equals(t) && !vt.IsSlice() } const ( + STATEMENT_TYPE_NOP StatementType = "nop" STATEMENT_TYPE_PROGRAM StatementType = "program" STATEMENT_TYPE_TYPE_DECLARATION StatementType = "type declaration" STATEMENT_TYPE_TYPE_DEFINITION StatementType = "type definition" + STATEMENT_TYPE_STRUCT_DECLARATION StatementType = "struct declaration" + STATEMENT_TYPE_STRUCT_DEFINITION StatementType = "struct definition" + STATEMENT_TYPE_STRUCT_ASSIGNMENT StatementType = "struct assignment" + STATEMENT_TYPE_STRUCT_EVALUATION StatementType = "struct evaluation" STATEMENT_TYPE_BOOL_LITERAL StatementType = "boolean" STATEMENT_TYPE_INT_LITERAL StatementType = "integer" STATEMENT_TYPE_STRING_LITERAL StatementType = "string" @@ -114,15 +272,6 @@ const ( ASSIGNMENT_TYPE_CALL AssignmentType = "call" ) -const ( - DATA_TYPE_UNKNOWN DataType = "unknown" - DATA_TYPE_MULTIPLE DataType = "multiple" - DATA_TYPE_BOOLEAN DataType = "bool" - DATA_TYPE_INTEGER DataType = "int" - DATA_TYPE_STRING DataType = "string" - DATA_TYPE_ERROR DataType = "error" -) - const ( COMPARE_OPERATOR_EQUAL CompareOperator = "==" COMPARE_OPERATOR_NOT_EQUAL CompareOperator = "!=" diff --git a/parser/write.go b/parser/write.go index d5fc30a..94de16b 100644 --- a/parser/write.go +++ b/parser/write.go @@ -11,7 +11,7 @@ func (w Write) StatementType() StatementType { } func (w Write) ValueType() ValueType { - return NewValueType(DATA_TYPE_ERROR, false) + return NewValueType(TypeError{}, false) } func (w Write) IsConstant() bool { diff --git a/tests/slices.go b/tests/slices.go index b2b5d16..79fc9ee 100644 --- a/tests/slices.go +++ b/tests/slices.go @@ -17,6 +17,20 @@ func testDefineSliceSuccess(t *testing.T, transpilerFunc transpilerFunc) { }) } +func testDefineSliceRowValuesSuccess(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + var a = []int{ + 1, + 2, + } + + print(a[0], a[1]) + `, func(output string, err error) { + require.Nil(t, err) + require.Equal(t, "1 2", output) + }) +} + func testSliceAssignValuesSuccess(t *testing.T, transpilerFunc transpilerFunc) { transpilerFunc(t, ` var a = []int{1, 2} diff --git a/tests/slices_linux_test.go b/tests/slices_linux_test.go index 2365b33..fc9f9d3 100644 --- a/tests/slices_linux_test.go +++ b/tests/slices_linux_test.go @@ -8,6 +8,10 @@ func TestDefineSliceSuccess(t *testing.T) { testDefineSliceSuccess(t, transpileBash) } +func TestDefineSliceRowValuesSuccess(t *testing.T) { + testDefineSliceRowValuesSuccess(t, transpileBash) +} + func TestSliceAssignValuesSuccess(t *testing.T) { testSliceAssignValuesSuccess(t, transpileBash) } diff --git a/tests/slices_windows_test.go b/tests/slices_windows_test.go index 4e371e2..ce34f65 100644 --- a/tests/slices_windows_test.go +++ b/tests/slices_windows_test.go @@ -8,6 +8,10 @@ func TestDefineSliceSuccess(t *testing.T) { testDefineSliceSuccess(t, transpileBatch) } +func TestDefineSliceRowValuesSuccess(t *testing.T) { + testDefineSliceRowValuesSuccess(t, transpileBatch) +} + func TestSliceAssignValuesSuccess(t *testing.T) { testSliceAssignValuesSuccess(t, transpileBatch) } diff --git a/tests/struct.go b/tests/struct.go new file mode 100644 index 0000000..90f2ff5 --- /dev/null +++ b/tests/struct.go @@ -0,0 +1,211 @@ +package tests + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func testDeclareAndDefineStructSuccess(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + c bool + d int + } + var s myStruct + + s.a = "Hello" + s.b = "World" + + print(s.a, s.b, s.c, s.d) + `, func(output string, err error) { + require.Nil(t, err) + require.Equal(t, "Hello World 0 0", output) + }) +} + +func testDeclareAndDefineStructWithValuesSuccess(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + c bool + d int + } + s := myStruct{ + a: "Hello", + b: "World", + } + + print(s.a, s.b, s.c, s.d) + `, func(output string, err error) { + require.Nil(t, err) + require.Equal(t, "Hello World 0 0", output) + }) +} + +func testDeclareAndDefineStructWithValuesOneLineSuccess(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + c bool + d int + } + s := myStruct{a: "Hello", b: "World"} + + print(s.a, s.b, s.c, s.d) + `, func(output string, err error) { + require.Nil(t, err) + require.Equal(t, "Hello World 0 0", output) + }) +} + +func testDeclareAndDefineStructSliceSuccess(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + c bool + d int + } + var s myStruct + var sl []myStruct + + s.a = "Hello" + s.b = "World" + s.d = 2 + + sl[1] = s + + for i, val := range sl { + print(val.a, val.b, val.c, val.d) + } + `, func(output string, err error) { + require.Nil(t, err) + require.Equal(t, "0 0\nHello World 0 2", output) + }) +} + +func testPassStructToFunctionSuccess(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + } + + func test(x myStruct) { + print(x.a, x.b) + + x.a = "Bye" + x.b = "Mars" + + print(x.a, x.b) + } + var s myStruct + + s.a = "Hello" + s.b = "World" + + print(s.a, s.b) + test(s) + print(s.a, s.b) + `, func(output string, err error) { + require.Nil(t, err) + require.Equal(t, "Hello World\nHello World\nBye Mars\nHello World", output) + }) +} + +func testReturnDifferentStructsSuccess(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + c bool + d int + } + count := 0 + + func test() myStruct { + s := myStruct{} + s.a = itoa(count) + + return s + } + + s1 := test() + print(s1.a) + count++ + s2 := test() + print(s1.a) + print(s2.a) + `, func(output string, err error) { + require.Nil(t, err) + require.Equal(t, "0\n0\n1", output) + }) +} + +func testStructFieldAssignedTwiceInInitializationFail(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + c bool + d int + } + s := myStruct{a: "Hello", a: "World"} + `, func(output string, err error) { + require.EqualError(t, shortenError(err), "a value has already been assigned to a") + }) +} + +func testStructUnknownFieldInInitializationFail(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + c bool + d int + } + s := myStruct{a: "Hello", x: "World"} + `, func(output string, err error) { + require.EqualError(t, shortenError(err), "struct field x doesn't exist") + }) +} + +func testStructFieldWrongTypeAssignmentInInitializationFail(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + c bool + d int + } + s := myStruct{a: "Hello", b: 1} + `, func(output string, err error) { + require.EqualError(t, shortenError(err), "expected string value but got int") + }) +} + +func testStructUnknownFieldInAssignmentFail(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + c bool + d int + } + var s myStruct + + s.x = "Test" + `, func(output string, err error) { + require.EqualError(t, shortenError(err), "struct field x doesn't exist") + }) +} + +func testStructFieldWrongTypeAssignmentInAssignmentFail(t *testing.T, transpilerFunc transpilerFunc) { + transpilerFunc(t, ` + type myStruct struct { + a, b string + c bool + d int + } + var s myStruct + + s.b = 1 + `, func(output string, err error) { + require.EqualError(t, shortenError(err), "expected string value but got int") + }) +} diff --git a/tests/struct_linux_test.go b/tests/struct_linux_test.go new file mode 100644 index 0000000..19e0d83 --- /dev/null +++ b/tests/struct_linux_test.go @@ -0,0 +1,49 @@ +package tests + +import ( + "testing" +) + +func TestDeclareAndDefineStructSuccess(t *testing.T) { + testDeclareAndDefineStructSuccess(t, transpileBash) +} + +func TestDeclareAndDefineStructWithValuesSuccess(t *testing.T) { + testDeclareAndDefineStructWithValuesSuccess(t, transpileBash) +} + +func TestDeclareAndDefineStructWithValuesOneLineSuccess(t *testing.T) { + testDeclareAndDefineStructWithValuesOneLineSuccess(t, transpileBash) +} + +func TestDeclareAndDefineStructSliceSuccess(t *testing.T) { + testDeclareAndDefineStructSliceSuccess(t, transpileBash) +} + +func TestPassStructToFunctionSuccess(t *testing.T) { + testPassStructToFunctionSuccess(t, transpileBash) +} + +func TestReturnDifferentStructsSuccess(t *testing.T) { + testReturnDifferentStructsSuccess(t, transpileBash) +} + +func TestStructFieldAssignedTwiceInInitializationFail(t *testing.T) { + testStructFieldAssignedTwiceInInitializationFail(t, transpileBash) +} + +func TestStructUnknownFieldInInitializationFail(t *testing.T) { + testStructUnknownFieldInInitializationFail(t, transpileBash) +} + +func TestStructFieldWrongTypeAssignmentInInitializationFail(t *testing.T) { + testStructFieldWrongTypeAssignmentInInitializationFail(t, transpileBash) +} + +func TestStructUnknownFieldInAssignmentFail(t *testing.T) { + testStructUnknownFieldInAssignmentFail(t, transpileBash) +} + +func TestStructFieldWrongTypeAssignmentInAssignmentFail(t *testing.T) { + testStructFieldWrongTypeAssignmentInAssignmentFail(t, transpileBash) +} diff --git a/tests/struct_windows_test.go b/tests/struct_windows_test.go new file mode 100644 index 0000000..4d57a09 --- /dev/null +++ b/tests/struct_windows_test.go @@ -0,0 +1,49 @@ +package tests + +import ( + "testing" +) + +func TestDeclareAndDefineStructSuccess(t *testing.T) { + testDeclareAndDefineStructSuccess(t, transpileBatch) +} + +func TestDeclareAndDefineStructWithValuesSuccess(t *testing.T) { + testDeclareAndDefineStructWithValuesSuccess(t, transpileBatch) +} + +func TestDeclareAndDefineStructWithValuesOneLineSuccess(t *testing.T) { + testDeclareAndDefineStructWithValuesOneLineSuccess(t, transpileBatch) +} + +func TestDeclareAndDefineStructSliceSuccess(t *testing.T) { + testDeclareAndDefineStructSliceSuccess(t, transpileBatch) +} + +func TestPassStructToFunctionSuccess(t *testing.T) { + testPassStructToFunctionSuccess(t, transpileBatch) +} + +func TestReturnDifferentStructsSuccess(t *testing.T) { + testReturnDifferentStructsSuccess(t, transpileBatch) +} + +func TestStructFieldAssignedTwiceInInitializationFail(t *testing.T) { + testStructFieldAssignedTwiceInInitializationFail(t, transpileBatch) +} + +func TestStructUnknownFieldInInitializationFail(t *testing.T) { + testStructUnknownFieldInInitializationFail(t, transpileBatch) +} + +func TestStructFieldWrongTypeAssignmentInInitializationFail(t *testing.T) { + testStructFieldWrongTypeAssignmentInInitializationFail(t, transpileBatch) +} + +func TestStructUnknownFieldInAssignmentFail(t *testing.T) { + testStructUnknownFieldInAssignmentFail(t, transpileBatch) +} + +func TestStructFieldWrongTypeAssignmentInAssignmentFail(t *testing.T) { + testStructFieldWrongTypeAssignmentInAssignmentFail(t, transpileBatch) +} diff --git a/tests/type_test.go b/tests/type_test.go index 5662ab8..b75e396 100644 --- a/tests/type_test.go +++ b/tests/type_test.go @@ -124,7 +124,7 @@ func testDeclareTypeTwiceFail(t *testing.T, transpilerFunc transpilerFunc) { type myType int type myType string `, func(output string, err error) { - require.EqualError(t, shortenError(err), "myType has already been defined") + require.EqualError(t, shortenError(err), "type myType has already been defined") }) } @@ -151,7 +151,7 @@ func testPassBaseTypeToFunctionFail(t *testing.T, transpilerFunc transpilerFunc) } test("test") `, func(output string, err error) { - require.EqualError(t, shortenError(err), "expected parameter of type myType (param) but got string") + require.EqualError(t, shortenError(err), "expected type of parameter param is myType but got string") }) } diff --git a/transpiler/converter.go b/transpiler/converter.go index c0561c4..7382ccf 100644 --- a/transpiler/converter.go +++ b/transpiler/converter.go @@ -46,6 +46,19 @@ func (rv ReturnValue) ValueType() parser.ValueType { return rv.valueType } +type StructValue struct { + name string + value string +} + +func (sv StructValue) Name() string { + return sv.name +} + +func (sv StructValue) Value() string { + return sv.value +} + type Converter interface { // Common methods StringToString(value string) string @@ -58,6 +71,7 @@ type Converter interface { VarDefinition(name string, value string, global bool) error VarAssignment(name string, value string, global bool) error SliceAssignment(name string, index string, value string, defaultValue string, global bool) error + StructAssignment(name string, field string, value string, global bool) error FuncStart(name string, params []string, returnTypes []parser.ValueType) error FuncEnd() error Return(values []ReturnValue) error @@ -88,6 +102,8 @@ type Converter interface { SliceInstantiation(values []string, valueUsed bool) (string, error) SliceEvaluation(name string, index string, valueUsed bool) (string, error) SliceLen(name string, valueUsed bool) (string, error) + StructInitialization(values []StructValue, valueUsed bool) (string, error) + StructEvaluation(name string, field string, valueUsed bool) (string, error) StringSubscript(value string, startIndex string, endIndex string, valueUsed bool) (string, error) StringLen(value string, valueUsed bool) (string, error) Group(value string, valueUsed bool) (string, error) diff --git a/transpiler/transpiler.go b/transpiler/transpiler.go index b674bc9..623aec9 100644 --- a/transpiler/transpiler.go +++ b/transpiler/transpiler.go @@ -1,6 +1,7 @@ package transpiler import ( + "errors" "fmt" "strconv" @@ -71,13 +72,39 @@ func (t *transpiler) evaluateValueTypeDefaultValue(valueType parser.ValueType) ( var defaultValue string conv := t.converter - switch valueType.DataType() { - case parser.DATA_TYPE_BOOLEAN: + switch valueType.Type().Kind() { + case parser.TypeKindBool: defaultValue = BoolToString(false) - case parser.DATA_TYPE_INTEGER: + case parser.TypeKindInt: defaultValue = IntToString(0) - case parser.DATA_TYPE_STRING: + case parser.TypeKindString: defaultValue = conv.StringToString("") + case parser.TypeKindStruct: + structDefinition, valid := valueType.Type().(parser.StructDefinition) + + if !valid { + return "", errors.New("struct declaration could not be evaluated") + } + values := []parser.StructValue{} + + for _, field := range structDefinition.Fields() { + fieldValueType := field.ValueType() + defaultValueTemp, err := t.evaluateValueTypeDefaultValue(fieldValueType) + + if err != nil { + return "", err + } + values = append(values, parser.NewStructValue(field.Name(), fieldValueType, parser.NewStringLiteral(defaultValueTemp))) + } + + // Create helper struct definition. + structInitialization := parser.NewStructInitialization(structDefinition, values...) + result, err := t.evaluateExpression(structInitialization, true) + + if err != nil { + return "", err + } + defaultValue = result.firstValue() default: return "", fmt.Errorf(`no default value defined for %s`, valueType.String()) } @@ -493,6 +520,20 @@ func (t *transpiler) evaluateSliceAssignment(assignment parser.SliceAssignment) return t.converter.SliceAssignment(assignment.LayerName(), indexResult.firstValue(), valueResult.firstValue(), defaultValue, assignment.Global()) } +func (t *transpiler) evaluateStructAssignment(assignment parser.StructAssignment) error { + value := assignment.Value() + valueResult, err := t.evaluateExpression(value.Value(), true) + + if err != nil { + return err + } + + if err != nil { + return err + } + return t.converter.StructAssignment(assignment.LayerName(), value.Name(), valueResult.firstValue(), assignment.Global()) +} + func (t *transpiler) evaluateConstEvaluation(evaluation parser.ConstEvaluation, valueUsed bool) (expressionResult, error) { // Map const evaluation to var evaluation since constant evaluation works the same. varEvaluation := parser.NewVariableEvaluation(evaluation.LayerName(), evaluation.ValueType(), evaluation.Layer(), evaluation.Public()) @@ -530,6 +571,15 @@ func (t *transpiler) evaluateSliceEvaluation(evaluation parser.SliceEvaluation, return newExpressionResult(s), err } +func (t *transpiler) evaluateStructEvaluation(evaluation parser.StructEvaluation, valueUsed bool) (expressionResult, error) { + s, err := t.converter.StructEvaluation(evaluation.LayerName(), evaluation.Field().Name(), valueUsed) + + if err != nil { + return expressionResult{}, err + } + return newExpressionResult(s), err +} + func (t *transpiler) evaluateStringSubscript(subscript parser.StringSubscript, valueUsed bool) (expressionResult, error) { startIndexResult, err := t.evaluateIndex(subscript.StartIndex(), true) @@ -619,15 +669,44 @@ func (t *transpiler) evaluateFunctionDefinition(functionDefinition parser.Functi func (t *transpiler) evaluateFunctionCall(functionCall parser.FunctionCall, valueUsed bool) (expressionResult, error) { name := functionCall.Name() + params := functionCall.Params() args := []string{} - for _, arg := range functionCall.Args() { + for i, arg := range functionCall.Args() { result, err := t.evaluateExpression(arg, true) + value := result.firstValue() if err != nil { return expressionResult{}, err } - args = append(args, result.firstValue()) + param := params[i] + + switch evaluationType := arg.ValueType().Type().(type) { + case parser.StructDefinition: + // If passed argument is a struct, the values need to be copied to avoid manipulation of the original. + paramName := param.LayerName() + + for _, field := range evaluationType.Fields() { + fieldName := field.Name() + fieldValue, err := t.converter.StructEvaluation(value, fieldName, true) + + if err != nil { + return expressionResult{}, nil + } + err = t.converter.StructAssignment(paramName, fieldName, fieldValue, false) + + if err != nil { + return expressionResult{}, err + } + } + evaluatedValue, err := t.converter.VarEvaluation(paramName, true, false) + + if err != nil { + return expressionResult{}, err + } + value = evaluatedValue + } + args = append(args, value) } returnTypes := functionCall.ReturnTypes() values, err := t.converter.FuncCall(name, args, returnTypes, valueUsed) @@ -693,6 +772,28 @@ func (t *transpiler) evaluateSliceInstantiation(instantiation parser.SliceInstan return newExpressionResult(s), nil } +func (t *transpiler) evaluateStructInitialization(definition parser.StructInitialization, valueUsed bool) (expressionResult, error) { + values := []StructValue{} + + for _, value := range definition.Values() { + result, err := t.evaluateExpression(value.Value(), true) + + if err != nil { + return expressionResult{}, err + } + values = append(values, StructValue{ + name: value.Name(), + value: result.firstValue(), + }) + } + s, err := t.converter.StructInitialization(values, valueUsed) + + if err != nil { + return expressionResult{}, err + } + return newExpressionResult(s), nil +} + func (t *transpiler) evaluateInput(input parser.Input, valueUsed bool) (expressionResult, error) { promptString := "" prompt := input.Prompt() @@ -821,6 +922,8 @@ func (t *transpiler) evaluate(statement parser.Statement) error { return t.evaluateVarAssignmentCallAssignment(statement.(parser.VariableAssignmentCallAssignment)) case parser.STATEMENT_TYPE_SLICE_ASSIGNMENT: return t.evaluateSliceAssignment(statement.(parser.SliceAssignment)) + case parser.STATEMENT_TYPE_STRUCT_ASSIGNMENT: + return t.evaluateStructAssignment(statement.(parser.StructAssignment)) case parser.STATEMENT_TYPE_FUNCTION_DEFINITION: return t.evaluateFunctionDefinition(statement.(parser.FunctionDefinition)) case parser.STATEMENT_TYPE_RETURN: @@ -876,6 +979,8 @@ func (t *transpiler) evaluateExpression(expression parser.Expression, valueUsed return t.evaluateVarEvaluation(expression.(parser.VariableEvaluation), valueUsed) case parser.STATEMENT_TYPE_SLICE_EVALUATION: return t.evaluateSliceEvaluation(expression.(parser.SliceEvaluation), valueUsed) + case parser.STATEMENT_TYPE_STRUCT_EVALUATION: + return t.evaluateStructEvaluation(expression.(parser.StructEvaluation), valueUsed) case parser.STATEMENT_TYPE_STRING_SUBSCRIPT: return t.evaluateStringSubscript(expression.(parser.StringSubscript), valueUsed) case parser.STATEMENT_TYPE_GROUP: @@ -886,6 +991,8 @@ func (t *transpiler) evaluateExpression(expression parser.Expression, valueUsed return t.evaluateAppCall(expression.(parser.AppCall), valueUsed) case parser.STATEMENT_TYPE_SLICE_INSTANTIATION: return t.evaluateSliceInstantiation(expression.(parser.SliceInstantiation), valueUsed) + case parser.STATEMENT_TYPE_STRUCT_DEFINITION: + return t.evaluateStructInitialization(expression.(parser.StructInitialization), valueUsed) case parser.STATEMENT_TYPE_INPUT: return t.evaluateInput(expression.(parser.Input), valueUsed) case parser.STATEMENT_TYPE_COPY: