mirror of
https://github.com/sigmasternchen/xml-programming
synced 2025-03-15 08:08:59 +00:00
initial commit
I wrote this a few year back (according to the mtime of the files Sep 12th 2021)
This commit is contained in:
commit
6ad0168eaf
14 changed files with 1621 additions and 0 deletions
38
cmd/main.go
Normal file
38
cmd/main.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"xml-programming/internal/analysis"
|
||||||
|
"xml-programming/internal/parser"
|
||||||
|
"xml-programming/internal/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
filename := flag.Arg(0)
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
content, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
program, err := parser.Parse(content)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//json, _ := json.MarshalIndent(program, "", " ")
|
||||||
|
//fmt.Println(string(json))
|
||||||
|
|
||||||
|
err = analysis.StaticAnalysis(program)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.Run(program)
|
||||||
|
}
|
293
internal/analysis/static.go
Normal file
293
internal/analysis/static.go
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
package analysis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"xml-programming/internal/ast"
|
||||||
|
"xml-programming/internal/scope"
|
||||||
|
"xml-programming/internal/values"
|
||||||
|
)
|
||||||
|
|
||||||
|
func analyseArithmetic(types []ast.Type) (ast.Type, error) {
|
||||||
|
isFloat := false
|
||||||
|
for _, _type := range types {
|
||||||
|
if !_type.IsNumber() {
|
||||||
|
return ast.Void, fmt.Errorf("can't do arithmetic on non-number types")
|
||||||
|
}
|
||||||
|
if _type == ast.Float {
|
||||||
|
isFloat = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isFloat {
|
||||||
|
return ast.Float, nil
|
||||||
|
} else {
|
||||||
|
return ast.Int, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func analyseComparison(types []ast.Type, operator ast.Operator) (ast.Type, error) {
|
||||||
|
if operator != ast.Equal {
|
||||||
|
for _, _type := range types {
|
||||||
|
if !_type.IsNumber() {
|
||||||
|
return ast.Void, fmt.Errorf("can not compare non-number types")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast.Bool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func analyseLogic(types []ast.Type) (ast.Type, error) {
|
||||||
|
for _, _type := range types {
|
||||||
|
if _type != ast.Bool {
|
||||||
|
return ast.Void, fmt.Errorf("can only do logic on bools")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ast.Bool, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func analyseExpression(expression ast.Expression, localScope *scope.Scope) (ast.Type, error) {
|
||||||
|
switch v := expression.(type) {
|
||||||
|
case ast.LiteralExpression:
|
||||||
|
return v.Type, nil
|
||||||
|
case ast.VariableExpression:
|
||||||
|
variable := localScope.GetVariable(v.Name)
|
||||||
|
if variable == nil {
|
||||||
|
return ast.Void, fmt.Errorf("name %s not found in local scope", v.Name)
|
||||||
|
}
|
||||||
|
return variable.Type, nil
|
||||||
|
case ast.FunctionCall:
|
||||||
|
function := localScope.GetFunction(v.Name)
|
||||||
|
if function == nil {
|
||||||
|
return ast.Void, fmt.Errorf("name %s not found in local scope", v.Name)
|
||||||
|
}
|
||||||
|
if len(function.Args) != len(v.Args) {
|
||||||
|
return ast.Void, fmt.Errorf("mismatched number of arguments for name %s", v.Name)
|
||||||
|
}
|
||||||
|
for i, _ := range function.Args {
|
||||||
|
expectedType := function.Args[i]
|
||||||
|
actualType, err := analyseExpression(v.Args[i], localScope)
|
||||||
|
if err != nil {
|
||||||
|
return ast.Void, err
|
||||||
|
}
|
||||||
|
if expectedType.Type != actualType {
|
||||||
|
return ast.Void, fmt.Errorf("mismatched types for argument %d of name %s", i + 1, v.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function.Return, nil
|
||||||
|
case ast.OperatorExpression:
|
||||||
|
types, err := analyseExpressionist(v.Exprs, localScope)
|
||||||
|
if err != nil {
|
||||||
|
return ast.Void, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(types) < 1 {
|
||||||
|
return ast.Void, fmt.Errorf("operator with no arguments is not valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v.Operator {
|
||||||
|
case ast.Add:
|
||||||
|
return analyseArithmetic(types)
|
||||||
|
case ast.Sub:
|
||||||
|
if len(types) != 2 {
|
||||||
|
return ast.Void, fmt.Errorf("sub is binary")
|
||||||
|
}
|
||||||
|
return analyseArithmetic(types)
|
||||||
|
case ast.Mul:
|
||||||
|
return analyseArithmetic(types)
|
||||||
|
case ast.Div:
|
||||||
|
if len(types) != 2 {
|
||||||
|
return ast.Void, fmt.Errorf("div is binary")
|
||||||
|
}
|
||||||
|
return analyseArithmetic(types)
|
||||||
|
case ast.Mod:
|
||||||
|
if len(types) != 2 {
|
||||||
|
return ast.Void, fmt.Errorf("mod is binary")
|
||||||
|
}
|
||||||
|
if types[0] == ast.Float || types[1] == ast.Float {
|
||||||
|
return ast.Void, fmt.Errorf("mod only supports int")
|
||||||
|
}
|
||||||
|
return ast.Int, nil
|
||||||
|
case ast.Concat:
|
||||||
|
return ast.String, nil
|
||||||
|
case ast.Equal:
|
||||||
|
if len(types) != 2 {
|
||||||
|
return ast.Void, fmt.Errorf("equal is binary")
|
||||||
|
}
|
||||||
|
return analyseComparison(types, v.Operator)
|
||||||
|
case ast.LessThan:
|
||||||
|
if len(types) != 2 {
|
||||||
|
return ast.Void, fmt.Errorf("less than is binary")
|
||||||
|
}
|
||||||
|
return analyseComparison(types, v.Operator)
|
||||||
|
case ast.GreaterThan:
|
||||||
|
if len(types) != 2 {
|
||||||
|
return ast.Void, fmt.Errorf("greater than is binary")
|
||||||
|
}
|
||||||
|
return analyseComparison(types, v.Operator)
|
||||||
|
case ast.Not:
|
||||||
|
if len(types) != 1 {
|
||||||
|
return ast.Void, fmt.Errorf("not is unary")
|
||||||
|
}
|
||||||
|
return analyseLogic(types)
|
||||||
|
case ast.And:
|
||||||
|
return analyseLogic(types)
|
||||||
|
case ast.Or:
|
||||||
|
return analyseLogic(types)
|
||||||
|
default:
|
||||||
|
return ast.Void, fmt.Errorf("not yet implemented")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ast.Void, fmt.Errorf("not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func analyseExpressionist(expressions []ast.Expression, localScope *scope.Scope) ([]ast.Type, error) {
|
||||||
|
var types []ast.Type
|
||||||
|
for _, expr := range expressions {
|
||||||
|
_type, err := analyseExpression(expr, localScope)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
types = append(types, _type)
|
||||||
|
}
|
||||||
|
return types, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func analyseStatement(statement ast.Statement, localScope *scope.Scope, currentFunction *scope.Function) error {
|
||||||
|
switch v := statement.(type) {
|
||||||
|
case ast.OutputStatement:
|
||||||
|
_, err := analyseExpressionist(v.Exprs, localScope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case ast.VariableDeclarationStatement:
|
||||||
|
if localScope.CurrentScopeHas(v.Name) {
|
||||||
|
return fmt.Errorf("name %s already exists in local scope", v.Name)
|
||||||
|
}
|
||||||
|
localScope.AddVariable(scope.Variable{
|
||||||
|
Name: v.Name,
|
||||||
|
Value: values.Value{
|
||||||
|
Type: v.Type,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case ast.VariableAssignmentStatement:
|
||||||
|
variable := localScope.GetVariable(v.Name)
|
||||||
|
if variable == nil {
|
||||||
|
return fmt.Errorf("name %s not found in local scope", v.Name)
|
||||||
|
}
|
||||||
|
_type, err := analyseExpression(v.Expr, localScope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _type != variable.Type {
|
||||||
|
return fmt.Errorf("type mismatch on assignment")
|
||||||
|
}
|
||||||
|
case ast.FunctionStatement:
|
||||||
|
if localScope.CurrentScopeHas(v.Name) {
|
||||||
|
return fmt.Errorf("name %s already exists in local scope", v.Name)
|
||||||
|
}
|
||||||
|
function := scope.Function{
|
||||||
|
Name: v.Name,
|
||||||
|
Args: v.Args,
|
||||||
|
Return: v.Returns,
|
||||||
|
}
|
||||||
|
localScope.AddFunction(function)
|
||||||
|
|
||||||
|
functionScope := scope.FromParent(localScope)
|
||||||
|
for _, arg := range v.Args {
|
||||||
|
functionScope.AddVariable(scope.Variable{
|
||||||
|
Name: arg.Name,
|
||||||
|
Value: values.Value{
|
||||||
|
Type: arg.Type,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err := analyseStatements(v.Body, functionScope, &function)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case ast.FunctionReturnStatement:
|
||||||
|
if currentFunction == nil {
|
||||||
|
return fmt.Errorf("can not return outside of function")
|
||||||
|
}
|
||||||
|
|
||||||
|
_type, err := analyseExpression(v.Expr, localScope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _type != currentFunction.Return {
|
||||||
|
return fmt.Errorf("return type mismatch in name %v", currentFunction.Name)
|
||||||
|
}
|
||||||
|
case ast.ConditionalStatement:
|
||||||
|
for _, _if := range v.Ifs {
|
||||||
|
_type, err := analyseExpression(_if.Expr, localScope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _type != ast.Bool {
|
||||||
|
return fmt.Errorf("condition type has to be bool")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = analyseStatements(_if.Then, localScope, currentFunction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v.Else != nil {
|
||||||
|
err := analyseStatements(v.Else, localScope, currentFunction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ast.LoopStatement:
|
||||||
|
_type, err := analyseExpression(v.LoopCondition, localScope)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _type != ast.Bool {
|
||||||
|
return fmt.Errorf("condition type has to be bool")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = analyseStatements(v.Body, localScope, currentFunction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case ast.ForStatement:
|
||||||
|
if localScope.CurrentScopeHas(v.Name) {
|
||||||
|
return fmt.Errorf("name %s already in local scope", v.Name)
|
||||||
|
}
|
||||||
|
localScope.AddVariable(scope.Variable{
|
||||||
|
Name: v.Name,
|
||||||
|
Value: values.Value{
|
||||||
|
Type: ast.Int,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
err := analyseStatements(v.Body, localScope, currentFunction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func analyseStatements(statements []ast.Statement, scope *scope.Scope, currentFunction *scope.Function) error {
|
||||||
|
for _, statement := range statements {
|
||||||
|
err := analyseStatement(statement, scope, currentFunction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func StaticAnalysis(program *ast.Program) error {
|
||||||
|
scope := scope.New()
|
||||||
|
|
||||||
|
return analyseStatements(program.Statements, scope, nil)
|
||||||
|
}
|
98
internal/ast/ast.go
Normal file
98
internal/ast/ast.go
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package ast
|
||||||
|
|
||||||
|
type Program struct {
|
||||||
|
Statements []Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
type Statement interface {
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutputStatement struct {
|
||||||
|
Exprs []Expression
|
||||||
|
}
|
||||||
|
var _ Statement = OutputStatement{}
|
||||||
|
|
||||||
|
type VariableDeclarationStatement struct {
|
||||||
|
Name string
|
||||||
|
Type Type
|
||||||
|
}
|
||||||
|
var _ Statement = VariableDeclarationStatement{}
|
||||||
|
|
||||||
|
type VariableAssignmentStatement struct {
|
||||||
|
Name string
|
||||||
|
Expr Expression
|
||||||
|
}
|
||||||
|
var _ Statement = VariableAssignmentStatement{}
|
||||||
|
|
||||||
|
type FunctionStatement struct {
|
||||||
|
Name string
|
||||||
|
Returns Type
|
||||||
|
Args []FunctionArg
|
||||||
|
Body []Statement
|
||||||
|
}
|
||||||
|
var _ Statement = FunctionStatement{}
|
||||||
|
|
||||||
|
type FunctionArg struct {
|
||||||
|
Name string
|
||||||
|
Type Type
|
||||||
|
}
|
||||||
|
|
||||||
|
type FunctionReturnStatement struct {
|
||||||
|
Expr Expression
|
||||||
|
}
|
||||||
|
var _ Statement = FunctionReturnStatement{}
|
||||||
|
|
||||||
|
type ConditionalStatement struct {
|
||||||
|
Ifs []ConditionIf
|
||||||
|
Else []Statement
|
||||||
|
}
|
||||||
|
var _ Statement = ConditionalStatement{}
|
||||||
|
|
||||||
|
type ConditionIf struct {
|
||||||
|
Expr Expression
|
||||||
|
Then []Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoopStatement struct {
|
||||||
|
LoopCondition Expression
|
||||||
|
Body []Statement
|
||||||
|
}
|
||||||
|
var _ Statement = LoopStatement{}
|
||||||
|
|
||||||
|
type ForStatement struct {
|
||||||
|
Name string
|
||||||
|
From int
|
||||||
|
To int
|
||||||
|
Body []Statement
|
||||||
|
}
|
||||||
|
var _ Statement = ForStatement{}
|
||||||
|
|
||||||
|
type Expression interface {
|
||||||
|
}
|
||||||
|
|
||||||
|
type LiteralExpression struct {
|
||||||
|
Type Type
|
||||||
|
String string
|
||||||
|
Bool bool
|
||||||
|
Int int
|
||||||
|
Float float32
|
||||||
|
}
|
||||||
|
var _ Expression = LiteralExpression{}
|
||||||
|
|
||||||
|
type VariableExpression struct {
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
var _ Expression = VariableExpression{}
|
||||||
|
|
||||||
|
type OperatorExpression struct {
|
||||||
|
Operator Operator
|
||||||
|
Exprs []Expression
|
||||||
|
}
|
||||||
|
var _ Expression = OperatorExpression{}
|
||||||
|
|
||||||
|
type FunctionCall struct {
|
||||||
|
Name string
|
||||||
|
Args []Expression
|
||||||
|
}
|
||||||
|
var _ Expression = FunctionCall{}
|
||||||
|
var _ Statement = FunctionCall{}
|
19
internal/ast/operators.go
Normal file
19
internal/ast/operators.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package ast
|
||||||
|
|
||||||
|
type Operator int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Add Operator = iota
|
||||||
|
Sub
|
||||||
|
Mul
|
||||||
|
Div
|
||||||
|
Mod
|
||||||
|
Concat
|
||||||
|
|
||||||
|
Equal
|
||||||
|
LessThan
|
||||||
|
GreaterThan
|
||||||
|
Not
|
||||||
|
Or
|
||||||
|
And
|
||||||
|
)
|
15
internal/ast/types.go
Normal file
15
internal/ast/types.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package ast
|
||||||
|
|
||||||
|
type Type int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Void Type = iota
|
||||||
|
String
|
||||||
|
Bool
|
||||||
|
Int
|
||||||
|
Float
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t Type) IsNumber() bool {
|
||||||
|
return t == Int || t == Float
|
||||||
|
}
|
141
internal/parser/elements.go
Normal file
141
internal/parser/elements.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import "encoding/xml"
|
||||||
|
|
||||||
|
type ProgramElement struct {
|
||||||
|
XMLName xml.Name `xml:"program"`
|
||||||
|
Statements []StatementElement `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatementElement struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
|
||||||
|
OutputStatementElement
|
||||||
|
VariableDeclarationElement
|
||||||
|
VariableAssignmentElement
|
||||||
|
FunctionElement
|
||||||
|
ConditionStatementElement
|
||||||
|
LoopStatementElement
|
||||||
|
ForStatementElement
|
||||||
|
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
|
||||||
|
Exprs []ExpressionElement `xml:",any"`
|
||||||
|
|
||||||
|
Body StatementBody
|
||||||
|
}
|
||||||
|
|
||||||
|
const OutputStatementElementName = "output"
|
||||||
|
type OutputStatementElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
const VariableDeclarationElementName = "declare"
|
||||||
|
type VariableDeclarationElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
const VariableAssignmentElementName = "assign"
|
||||||
|
type VariableAssignmentElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
const LiteralExpressionStringElementName = "string"
|
||||||
|
const LiteralExpressionBoolElementName = "bool"
|
||||||
|
const LiteralExpressionIntElementName = "int"
|
||||||
|
const LiteralExpressionFloatElementName = "float"
|
||||||
|
const VariableExpressionElementName = "var"
|
||||||
|
const OperatorExpressionAddElementName = "add"
|
||||||
|
const OperatorExpressionSubElementName = "sub"
|
||||||
|
const OperatorExpressionMulElementName = "mul"
|
||||||
|
const OperatorExpressionDivElementName = "div"
|
||||||
|
const OperatorExpressionModElementName = "mod"
|
||||||
|
const OperatorExpressionConcatElementName = "concat"
|
||||||
|
const OperatorExpressionEqualElementName = "equal"
|
||||||
|
const OperatorExpressionGreaterThanElementName = "gt"
|
||||||
|
const OperatorExpressionLessThanElementName = "lt"
|
||||||
|
const OperatorExpressionNotElementName = "not"
|
||||||
|
const OperatorExpressionAndElementName = "and"
|
||||||
|
const OperatorExpressionOrElementName = "or"
|
||||||
|
const FunctionCallExpressionElementName = "call"
|
||||||
|
type ExpressionElement struct {
|
||||||
|
XMLName xml.Name
|
||||||
|
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
|
||||||
|
Content string `xml:",chardata"`
|
||||||
|
Exprs []ExpressionElement `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const FunctionElementName = "func"
|
||||||
|
type FunctionElement struct {
|
||||||
|
Args FunctionArgs `xml:"args"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FunctionArgs struct {
|
||||||
|
Returns FunctionReturnType `xml:"returns"`
|
||||||
|
Args []FunctionArg `xml:"arg"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FunctionArg struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FunctionReturnType struct {
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatementBody struct {
|
||||||
|
XMLName xml.Name `xml:"body"`
|
||||||
|
Statements []StatementElement `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const FunctionReturnElementName = "return"
|
||||||
|
type FunctionReturnElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
const FunctionCallStatementElementName = "call"
|
||||||
|
type FunctionCallStatementElement struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConditionStatementElementName = "switch"
|
||||||
|
type ConditionStatementElement struct {
|
||||||
|
Ifs []ConditionStatementIfClauseElement `xml:"if"`
|
||||||
|
Else *ConditionStatementIfClauseElement `xml:"else"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConditionStatementIfClauseElement struct {
|
||||||
|
Condition ConditionStatementConditionElement `xml:"cond"`
|
||||||
|
Then ConditionStatementThenElement
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConditionStatementElseClauseElement struct {
|
||||||
|
Then ConditionStatementThenElement
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConditionStatementConditionElement struct {
|
||||||
|
Expr ExpressionElement `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConditionStatementThenElement struct {
|
||||||
|
XMLName xml.Name `xml:"then"`
|
||||||
|
Statements []StatementElement `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoopStatementElementName = "loop"
|
||||||
|
type LoopStatementElement struct {
|
||||||
|
Condition LoopConditionElement `xml:"cond"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoopConditionElement struct {
|
||||||
|
Expr ExpressionElement `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoopBodyElement struct {
|
||||||
|
Statements []StatementElement `xml:",any"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const ForStatementElementName = "for"
|
||||||
|
type ForStatementElement struct {
|
||||||
|
To int `xml:"to,attr"`
|
||||||
|
From int `xml:"from,attr"`
|
||||||
|
}
|
301
internal/parser/parser.go
Normal file
301
internal/parser/parser.go
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"xml-programming/internal/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Parse(content []byte) (*ast.Program, error) {
|
||||||
|
var programElement ProgramElement
|
||||||
|
err := xml.Unmarshal(content, &programElement)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//json, _ := json.MarshalIndent(programElement, "", " ")
|
||||||
|
//fmt.Println(string(json))
|
||||||
|
|
||||||
|
var program ast.Program
|
||||||
|
program.Statements, err = ParseStatements(programElement.Statements)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &program, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseStatements(statementsElements []StatementElement) ([]ast.Statement, error) {
|
||||||
|
var statements []ast.Statement
|
||||||
|
for _, element := range statementsElements {
|
||||||
|
statement, err := ParseStatement(element)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
statements = append(statements, statement)
|
||||||
|
}
|
||||||
|
|
||||||
|
return statements, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseType(str string) (ast.Type, error) {
|
||||||
|
switch str {
|
||||||
|
case "string":
|
||||||
|
return ast.String, nil
|
||||||
|
case "bool":
|
||||||
|
return ast.Bool, nil
|
||||||
|
case "int":
|
||||||
|
return ast.Int, nil
|
||||||
|
case "float":
|
||||||
|
return ast.Float, nil
|
||||||
|
default:
|
||||||
|
return ast.Void, fmt.Errorf("unknown type: %v", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseStatement(statement StatementElement) (ast.Statement, error) {
|
||||||
|
switch statement.XMLName.Local {
|
||||||
|
case OutputStatementElementName:
|
||||||
|
var exprs []ast.Expression
|
||||||
|
for _, exprElement := range statement.Exprs {
|
||||||
|
expr, err := ParseExpression(exprElement)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
exprs = append(exprs, expr)
|
||||||
|
}
|
||||||
|
return ast.OutputStatement{
|
||||||
|
Exprs: exprs,
|
||||||
|
}, nil
|
||||||
|
case VariableDeclarationElementName:
|
||||||
|
t, err := ParseType(statement.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ast.VariableDeclarationStatement{
|
||||||
|
Name: statement.Name,
|
||||||
|
Type: t,
|
||||||
|
}, nil
|
||||||
|
case VariableAssignmentElementName:
|
||||||
|
if len(statement.Exprs) != 1 {
|
||||||
|
return nil, errors.New("variable assignments must have exactly one expression")
|
||||||
|
}
|
||||||
|
expr, err := ParseExpression(statement.Exprs[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ast.VariableAssignmentStatement{
|
||||||
|
Name: statement.Name,
|
||||||
|
Expr: expr,
|
||||||
|
}, nil
|
||||||
|
case FunctionElementName:
|
||||||
|
var err error
|
||||||
|
var args []ast.FunctionArg
|
||||||
|
for _, argElement := range statement.Args.Args {
|
||||||
|
arg := ast.FunctionArg{
|
||||||
|
Name: argElement.Name,
|
||||||
|
}
|
||||||
|
arg.Type, err = ParseType(argElement.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't parse function argument list: %w", err)
|
||||||
|
}
|
||||||
|
args = append(args, arg)
|
||||||
|
}
|
||||||
|
returns, err := ParseType(statement.Args.Returns.Type)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't parse function return type: %w", err)
|
||||||
|
}
|
||||||
|
var body []ast.Statement
|
||||||
|
for _, statementElement := range statement.Body.Statements {
|
||||||
|
bodyStatement, err := ParseStatement(statementElement)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body = append(body, bodyStatement)
|
||||||
|
}
|
||||||
|
return ast.FunctionStatement{
|
||||||
|
Name: statement.Name,
|
||||||
|
Returns: returns,
|
||||||
|
Args: args,
|
||||||
|
Body: body,
|
||||||
|
}, nil
|
||||||
|
case FunctionReturnElementName:
|
||||||
|
if len(statement.Exprs) != 1 {
|
||||||
|
return nil, errors.New("a return statement must have exactly one expression")
|
||||||
|
}
|
||||||
|
expr, err := ParseExpression(statement.Exprs[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ast.FunctionReturnStatement{
|
||||||
|
Expr: expr,
|
||||||
|
}, nil
|
||||||
|
case FunctionCallStatementElementName:
|
||||||
|
exprs, err := ParseExpressionList(statement.Exprs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ast.FunctionCall{
|
||||||
|
Name: statement.Name,
|
||||||
|
Args: exprs,
|
||||||
|
}, nil
|
||||||
|
case ConditionStatementElementName:
|
||||||
|
conditional := ast.ConditionalStatement{}
|
||||||
|
|
||||||
|
for _, _if := range statement.Ifs {
|
||||||
|
then, err := ParseStatements(_if.Then.Statements)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
expr, err := ParseExpression(_if.Condition.Expr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conditional.Ifs = append(conditional.Ifs, ast.ConditionIf{
|
||||||
|
Expr: expr,
|
||||||
|
Then: then,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if statement.Else != nil {
|
||||||
|
then, err := ParseStatements(statement.Else.Then.Statements)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conditional.Else = then
|
||||||
|
}
|
||||||
|
|
||||||
|
return conditional, nil
|
||||||
|
case LoopStatementElementName:
|
||||||
|
expr, err := ParseExpression(statement.LoopStatementElement.Condition.Expr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := ParseStatements(statement.Body.Statements)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ast.LoopStatement{
|
||||||
|
LoopCondition: expr,
|
||||||
|
Body: body,
|
||||||
|
}, nil
|
||||||
|
case ForStatementElementName:
|
||||||
|
body, err := ParseStatements(statement.Body.Statements)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ast.ForStatement{
|
||||||
|
From: statement.From,
|
||||||
|
To: statement.To,
|
||||||
|
Name: statement.Name,
|
||||||
|
Body: body,
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown statement: <%v>", statement.XMLName.Local)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseExpressionList(exprList []ExpressionElement) ([]ast.Expression, error) {
|
||||||
|
exprs := make([]ast.Expression, len(exprList))
|
||||||
|
|
||||||
|
var err error
|
||||||
|
for i, expr := range exprList {
|
||||||
|
exprs[i], err = ParseExpression(expr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse sub expression: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return exprs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseOperatorExpression(exprElement ExpressionElement, operator ast.Operator) (ast.Expression, error) {
|
||||||
|
exprs, err := ParseExpressionList(exprElement.Exprs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ast.OperatorExpression{
|
||||||
|
Operator: operator,
|
||||||
|
Exprs: exprs,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseExpression(exprElement ExpressionElement) (ast.Expression, error) {
|
||||||
|
switch exprElement.XMLName.Local {
|
||||||
|
case LiteralExpressionStringElementName:
|
||||||
|
return ast.LiteralExpression{
|
||||||
|
Type: ast.String,
|
||||||
|
String: exprElement.Content,
|
||||||
|
}, nil
|
||||||
|
case LiteralExpressionBoolElementName:
|
||||||
|
val, err := strconv.ParseBool(strings.TrimSpace(exprElement.Content))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse int value: %w", err)
|
||||||
|
}
|
||||||
|
return ast.LiteralExpression{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: val,
|
||||||
|
}, nil
|
||||||
|
case LiteralExpressionIntElementName:
|
||||||
|
val, err := strconv.Atoi(strings.TrimSpace(exprElement.Content))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse int value: %w", err)
|
||||||
|
}
|
||||||
|
return ast.LiteralExpression{
|
||||||
|
Type: ast.Int,
|
||||||
|
Int: val,
|
||||||
|
}, nil
|
||||||
|
case LiteralExpressionFloatElementName:
|
||||||
|
val, err := strconv.ParseFloat(strings.TrimSpace(exprElement.Content), 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse float value: %w", err)
|
||||||
|
}
|
||||||
|
return ast.LiteralExpression{
|
||||||
|
Type: ast.Float,
|
||||||
|
Float: float32(val),
|
||||||
|
}, nil
|
||||||
|
case VariableExpressionElementName:
|
||||||
|
return ast.VariableExpression{
|
||||||
|
Name: exprElement.Name,
|
||||||
|
}, nil
|
||||||
|
case OperatorExpressionAddElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.Add)
|
||||||
|
case OperatorExpressionSubElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.Sub)
|
||||||
|
case OperatorExpressionMulElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.Mul)
|
||||||
|
case OperatorExpressionDivElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.Div)
|
||||||
|
case OperatorExpressionModElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.Mod)
|
||||||
|
case OperatorExpressionConcatElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.Concat)
|
||||||
|
case OperatorExpressionEqualElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.Equal)
|
||||||
|
case OperatorExpressionGreaterThanElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.GreaterThan)
|
||||||
|
case OperatorExpressionLessThanElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.LessThan)
|
||||||
|
case OperatorExpressionNotElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.Not)
|
||||||
|
case OperatorExpressionAndElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.And)
|
||||||
|
case OperatorExpressionOrElementName:
|
||||||
|
return ParseOperatorExpression(exprElement, ast.Or)
|
||||||
|
case FunctionCallExpressionElementName:
|
||||||
|
exprs, err := ParseExpressionList(exprElement.Exprs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ast.FunctionCall{
|
||||||
|
Name: exprElement.Name,
|
||||||
|
Args: exprs,
|
||||||
|
}, nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unknown expression type")
|
||||||
|
}
|
||||||
|
}
|
86
internal/scope/scope.go
Normal file
86
internal/scope/scope.go
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package scope
|
||||||
|
|
||||||
|
import (
|
||||||
|
"xml-programming/internal/ast"
|
||||||
|
"xml-programming/internal/values"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Function struct {
|
||||||
|
Name string
|
||||||
|
Args []ast.FunctionArg
|
||||||
|
Return ast.Type
|
||||||
|
Body []ast.Statement
|
||||||
|
}
|
||||||
|
|
||||||
|
type Variable struct {
|
||||||
|
Name string
|
||||||
|
values.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
type Scope struct {
|
||||||
|
functions []Function
|
||||||
|
variables []Variable
|
||||||
|
|
||||||
|
parentScope *Scope
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scope) CurrentScopeHas(name string) bool {
|
||||||
|
for _, f := range s.functions {
|
||||||
|
if f.Name == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range s.variables {
|
||||||
|
if v.Name == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scope) GetFunction(name string) *Function {
|
||||||
|
for _, f := range s.functions {
|
||||||
|
if f.Name == name {
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.parentScope != nil {
|
||||||
|
return s.parentScope.GetFunction(name)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scope) GetVariable(name string) *Variable {
|
||||||
|
for i, v := range s.variables {
|
||||||
|
if v.Name == name {
|
||||||
|
return &s.variables[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.parentScope != nil {
|
||||||
|
return s.parentScope.GetVariable(name)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scope) AddVariable(variable Variable) {
|
||||||
|
s.variables = append(s.variables, variable)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Scope) AddFunction(function Function) {
|
||||||
|
s.functions = append(s.functions, function)
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() *Scope {
|
||||||
|
return &Scope{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromParent(scope *Scope) *Scope {
|
||||||
|
return &Scope{parentScope: scope}
|
||||||
|
}
|
||||||
|
|
36
internal/values/values.go
Normal file
36
internal/values/values.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package values
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"xml-programming/internal/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Value struct {
|
||||||
|
Type ast.Type
|
||||||
|
|
||||||
|
String string
|
||||||
|
Bool bool
|
||||||
|
Int int
|
||||||
|
Float float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromLiteralExpression(expression ast.LiteralExpression) Value {
|
||||||
|
value := Value{
|
||||||
|
Type: expression.Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch expression.Type {
|
||||||
|
case ast.String:
|
||||||
|
value.String = expression.String
|
||||||
|
case ast.Int:
|
||||||
|
value.Int = expression.Int
|
||||||
|
case ast.Bool:
|
||||||
|
value.Bool = expression.Bool
|
||||||
|
case ast.Float:
|
||||||
|
value.Float = expression.Float
|
||||||
|
default:
|
||||||
|
fmt.Println("unknown type: ", expression.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
393
internal/vm/exprs.go
Normal file
393
internal/vm/exprs.go
Normal file
|
@ -0,0 +1,393 @@
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"xml-programming/internal/ast"
|
||||||
|
"xml-programming/internal/scope"
|
||||||
|
"xml-programming/internal/values"
|
||||||
|
)
|
||||||
|
|
||||||
|
func evaluateAddExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
isFloat := false
|
||||||
|
|
||||||
|
var args []values.Value
|
||||||
|
|
||||||
|
for _, _arg := range expression.Exprs {
|
||||||
|
arg := evaluateExpression(_arg, localScope)
|
||||||
|
if arg.Type == ast.Float {
|
||||||
|
isFloat = true
|
||||||
|
}
|
||||||
|
args = append(args, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFloat {
|
||||||
|
var sum float32 = 0
|
||||||
|
for _, arg := range args {
|
||||||
|
if arg.Type == ast.Int {
|
||||||
|
sum += float32(arg.Int)
|
||||||
|
} else {
|
||||||
|
sum += arg.Float
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Float,
|
||||||
|
Float: sum,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var sum int = 0
|
||||||
|
for _, arg := range args {
|
||||||
|
// all ints
|
||||||
|
sum += arg.Int
|
||||||
|
}
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Int,
|
||||||
|
Int: sum,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateSubExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
isFloat := false
|
||||||
|
|
||||||
|
arg1 := evaluateExpression(expression.Exprs[0], localScope)
|
||||||
|
if arg1.Type == ast.Float {
|
||||||
|
isFloat = true
|
||||||
|
}
|
||||||
|
arg2 := evaluateExpression(expression.Exprs[1], localScope)
|
||||||
|
if arg2.Type == ast.Float {
|
||||||
|
isFloat = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFloat {
|
||||||
|
var result float32
|
||||||
|
|
||||||
|
if arg1.Type == ast.Int {
|
||||||
|
result = float32(arg1.Int)
|
||||||
|
} else {
|
||||||
|
result = arg1.Float
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg2.Type == ast.Int {
|
||||||
|
result -= float32(arg1.Int)
|
||||||
|
} else {
|
||||||
|
result -= arg2.Float
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Float,
|
||||||
|
Float: result,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var result int = arg1.Int
|
||||||
|
result -= arg2.Int
|
||||||
|
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Int,
|
||||||
|
Int: result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateMulExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
isFloat := false
|
||||||
|
|
||||||
|
var args []values.Value
|
||||||
|
|
||||||
|
for _, _arg := range expression.Exprs {
|
||||||
|
arg := evaluateExpression(_arg, localScope)
|
||||||
|
if arg.Type == ast.Float {
|
||||||
|
isFloat = true
|
||||||
|
}
|
||||||
|
args = append(args, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFloat {
|
||||||
|
var product float32 = 1
|
||||||
|
for _, arg := range args {
|
||||||
|
if arg.Type == ast.Int {
|
||||||
|
product *= float32(arg.Int)
|
||||||
|
} else {
|
||||||
|
product *= arg.Float
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Float,
|
||||||
|
Float: product,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var product int = 1
|
||||||
|
for _, arg := range args {
|
||||||
|
// all ints
|
||||||
|
product *= arg.Int
|
||||||
|
}
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Int,
|
||||||
|
Int: product,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateDivExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
isFloat := false
|
||||||
|
|
||||||
|
arg1 := evaluateExpression(expression.Exprs[0], localScope)
|
||||||
|
if arg1.Type == ast.Float {
|
||||||
|
isFloat = true
|
||||||
|
}
|
||||||
|
arg2 := evaluateExpression(expression.Exprs[1], localScope)
|
||||||
|
if arg2.Type == ast.Float {
|
||||||
|
isFloat = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isFloat {
|
||||||
|
var result float32
|
||||||
|
|
||||||
|
if arg1.Type == ast.Int {
|
||||||
|
result = float32(arg1.Int)
|
||||||
|
} else {
|
||||||
|
result = arg1.Float
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg2.Type == ast.Int {
|
||||||
|
result /= float32(arg1.Int)
|
||||||
|
} else {
|
||||||
|
result /= arg2.Float
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Float,
|
||||||
|
Float: result,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var result int = arg1.Int
|
||||||
|
result /= arg2.Int
|
||||||
|
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Int,
|
||||||
|
Int: result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateModExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
arg1 := evaluateExpression(expression.Exprs[0], localScope)
|
||||||
|
arg2 := evaluateExpression(expression.Exprs[1], localScope)
|
||||||
|
|
||||||
|
var result int = arg1.Int
|
||||||
|
result %= arg2.Int
|
||||||
|
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Int,
|
||||||
|
Int: result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateConcatExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
builder := strings.Builder{}
|
||||||
|
|
||||||
|
for _, _arg := range expression.Exprs {
|
||||||
|
arg := evaluateExpression(_arg, localScope)
|
||||||
|
switch arg.Type {
|
||||||
|
case ast.String:
|
||||||
|
builder.WriteString(arg.String)
|
||||||
|
case ast.Bool:
|
||||||
|
builder.WriteString(strconv.FormatBool(arg.Bool))
|
||||||
|
case ast.Int:
|
||||||
|
builder.WriteString(strconv.FormatInt(int64(arg.Int), 10))
|
||||||
|
case ast.Float:
|
||||||
|
builder.WriteString(strconv.FormatFloat(float64(arg.Float), 'e', 4, 32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.String,
|
||||||
|
String: builder.String(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateEqualExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
arg1 := evaluateExpression(expression.Exprs[0], localScope)
|
||||||
|
arg2 := evaluateExpression(expression.Exprs[1], localScope)
|
||||||
|
|
||||||
|
if arg1.Type == ast.Int {
|
||||||
|
if arg2.Type == ast.Float {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: float32(arg1.Int) == arg2.Float,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: arg1.Int == arg2.Int,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if arg2.Type == ast.Float {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: arg1.Float == arg2.Float,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: arg1.Float == float32(arg2.Int),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateGreaterThanExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
arg1 := evaluateExpression(expression.Exprs[0], localScope)
|
||||||
|
arg2 := evaluateExpression(expression.Exprs[1], localScope)
|
||||||
|
|
||||||
|
if arg1.Type == ast.Int {
|
||||||
|
if arg2.Type == ast.Float {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: float32(arg1.Int) > arg2.Float,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: arg1.Int > arg2.Int,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if arg2.Type == ast.Float {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: arg1.Float > arg2.Float,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: arg1.Float > float32(arg2.Int),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateLessThanExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
arg1 := evaluateExpression(expression.Exprs[0], localScope)
|
||||||
|
arg2 := evaluateExpression(expression.Exprs[1], localScope)
|
||||||
|
|
||||||
|
if arg1.Type == ast.Int {
|
||||||
|
if arg2.Type == ast.Float {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: float32(arg1.Int) < arg2.Float,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: arg1.Int < arg2.Int,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if arg2.Type == ast.Float {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: arg1.Float < arg2.Float,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: arg1.Float < float32(arg2.Int),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateNotExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
arg := evaluateExpression(expression.Exprs[0], localScope)
|
||||||
|
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: !arg.Bool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateAndExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
for _, expr := range expression.Exprs {
|
||||||
|
arg := evaluateExpression(expr, localScope)
|
||||||
|
if !arg.Bool {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateOrExpression(expression ast.OperatorExpression, localScope *scope.Scope) values.Value {
|
||||||
|
for _, expr := range expression.Exprs {
|
||||||
|
arg := evaluateExpression(expr, localScope)
|
||||||
|
if arg.Bool {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Bool,
|
||||||
|
Bool: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateExpression(expression ast.Expression, localScope *scope.Scope) values.Value {
|
||||||
|
switch v := expression.(type) {
|
||||||
|
case ast.LiteralExpression:
|
||||||
|
return values.FromLiteralExpression(v)
|
||||||
|
case ast.VariableExpression:
|
||||||
|
return localScope.GetVariable(v.Name).Value
|
||||||
|
case ast.OperatorExpression:
|
||||||
|
switch v.Operator {
|
||||||
|
case ast.Add:
|
||||||
|
return evaluateAddExpression(v, localScope)
|
||||||
|
case ast.Sub:
|
||||||
|
return evaluateSubExpression(v, localScope)
|
||||||
|
case ast.Mul:
|
||||||
|
return evaluateMulExpression(v, localScope)
|
||||||
|
case ast.Div:
|
||||||
|
return evaluateDivExpression(v, localScope)
|
||||||
|
case ast.Mod:
|
||||||
|
return evaluateModExpression(v, localScope)
|
||||||
|
case ast.Concat:
|
||||||
|
return evaluateConcatExpression(v, localScope)
|
||||||
|
case ast.Equal:
|
||||||
|
return evaluateEqualExpression(v, localScope)
|
||||||
|
case ast.GreaterThan:
|
||||||
|
return evaluateGreaterThanExpression(v, localScope)
|
||||||
|
case ast.LessThan:
|
||||||
|
return evaluateLessThanExpression(v, localScope)
|
||||||
|
case ast.Not:
|
||||||
|
return evaluateNotExpression(v, localScope)
|
||||||
|
case ast.And:
|
||||||
|
return evaluateAndExpression(v, localScope)
|
||||||
|
case ast.Or:
|
||||||
|
return evaluateOrExpression(v, localScope)
|
||||||
|
default:
|
||||||
|
fmt.Println(v)
|
||||||
|
panic("not yet implemented")
|
||||||
|
}
|
||||||
|
case ast.FunctionCall:
|
||||||
|
var args []values.Value
|
||||||
|
for _, arg := range v.Args {
|
||||||
|
args = append(args, evaluateExpression(arg, localScope))
|
||||||
|
}
|
||||||
|
|
||||||
|
return callFunction(v.Name, localScope, args)
|
||||||
|
default:
|
||||||
|
fmt.Println(expression)
|
||||||
|
panic("not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
28
internal/vm/functions.go
Normal file
28
internal/vm/functions.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"xml-programming/internal/ast"
|
||||||
|
"xml-programming/internal/scope"
|
||||||
|
"xml-programming/internal/values"
|
||||||
|
)
|
||||||
|
|
||||||
|
func callFunction(name string, localScope *scope.Scope, args []values.Value) values.Value {
|
||||||
|
function := localScope.GetFunction(name)
|
||||||
|
functionScope := scope.FromParent(localScope)
|
||||||
|
|
||||||
|
for i, arg := range args {
|
||||||
|
functionScope.AddVariable(scope.Variable{
|
||||||
|
Name: function.Args[i].Name,
|
||||||
|
Value: arg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
result := executeStatements(function.Body, functionScope)
|
||||||
|
if result != nil {
|
||||||
|
return *result
|
||||||
|
} else {
|
||||||
|
return values.Value{
|
||||||
|
Type: ast.Void,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
111
internal/vm/statements.go
Normal file
111
internal/vm/statements.go
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"xml-programming/internal/ast"
|
||||||
|
"xml-programming/internal/scope"
|
||||||
|
"xml-programming/internal/values"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printValue(value values.Value) {
|
||||||
|
switch value.Type {
|
||||||
|
case ast.String:
|
||||||
|
fmt.Print(value.String)
|
||||||
|
case ast.Int:
|
||||||
|
fmt.Print(value.Int)
|
||||||
|
case ast.Float:
|
||||||
|
fmt.Print(value.Float)
|
||||||
|
case ast.Bool:
|
||||||
|
fmt.Print(value.Bool)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeStatement(statement ast.Statement, localScope *scope.Scope) *values.Value {
|
||||||
|
switch v := statement.(type) {
|
||||||
|
case ast.OutputStatement:
|
||||||
|
for _, _arg := range v.Exprs {
|
||||||
|
arg := evaluateExpression(_arg, localScope)
|
||||||
|
printValue(arg)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
case ast.VariableDeclarationStatement:
|
||||||
|
localScope.AddVariable(scope.Variable{
|
||||||
|
Name: v.Name,
|
||||||
|
Value: values.Value{
|
||||||
|
Type: v.Type,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
case ast.VariableAssignmentStatement:
|
||||||
|
arg := evaluateExpression(v.Expr, localScope)
|
||||||
|
variable := localScope.GetVariable(v.Name)
|
||||||
|
variable.Value = arg
|
||||||
|
case ast.FunctionStatement:
|
||||||
|
localScope.AddFunction(scope.Function{
|
||||||
|
Name: v.Name,
|
||||||
|
Args: v.Args,
|
||||||
|
Return: v.Returns,
|
||||||
|
Body: v.Body,
|
||||||
|
})
|
||||||
|
case ast.FunctionReturnStatement:
|
||||||
|
arg := evaluateExpression(v.Expr, localScope)
|
||||||
|
return &arg
|
||||||
|
case ast.FunctionCall:
|
||||||
|
var args []values.Value
|
||||||
|
for _, arg := range v.Args {
|
||||||
|
args = append(args, evaluateExpression(arg, localScope))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = callFunction(v.Name, localScope, args)
|
||||||
|
case ast.ConditionalStatement:
|
||||||
|
for _, _if := range v.Ifs {
|
||||||
|
arg := evaluateExpression(_if.Expr, localScope)
|
||||||
|
if arg.Bool {
|
||||||
|
return executeStatements(_if.Then, localScope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return executeStatements(v.Else, localScope)
|
||||||
|
case ast.LoopStatement:
|
||||||
|
for {
|
||||||
|
arg := evaluateExpression(v.LoopCondition, localScope)
|
||||||
|
if !arg.Bool {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
result := executeStatements(v.Body, localScope)
|
||||||
|
if result != nil {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ast.ForStatement:
|
||||||
|
for i := v.From; i < v.To; i++ {
|
||||||
|
forScope := scope.FromParent(localScope)
|
||||||
|
forScope.AddVariable(scope.Variable{
|
||||||
|
Name: v.Name,
|
||||||
|
Value: values.Value{
|
||||||
|
Type: ast.Int,
|
||||||
|
Int: i,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
result := executeStatements(v.Body, forScope)
|
||||||
|
if result != nil {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Println(v)
|
||||||
|
panic("not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeStatements(statements []ast.Statement, localScope *scope.Scope) *values.Value {
|
||||||
|
for _, statement := range statements {
|
||||||
|
returnValue := executeStatement(statement, localScope)
|
||||||
|
if returnValue != nil {
|
||||||
|
return returnValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
11
internal/vm/vm.go
Normal file
11
internal/vm/vm.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"xml-programming/internal/ast"
|
||||||
|
"xml-programming/internal/scope"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Run(program *ast.Program) {
|
||||||
|
localScope := scope.New()
|
||||||
|
_ = executeStatements(program.Statements, localScope)
|
||||||
|
}
|
51
test.xml
Normal file
51
test.xml
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<program>
|
||||||
|
<func name="facc">
|
||||||
|
<args>
|
||||||
|
<arg name="i" type="int"/>
|
||||||
|
<returns type="int"/>
|
||||||
|
</args>
|
||||||
|
<body>
|
||||||
|
<switch>
|
||||||
|
<if>
|
||||||
|
<cond>
|
||||||
|
<lt>
|
||||||
|
<var name="i"/>
|
||||||
|
<int>1</int>
|
||||||
|
</lt>
|
||||||
|
</cond>
|
||||||
|
<then>
|
||||||
|
<return>
|
||||||
|
<int>1</int>
|
||||||
|
</return>
|
||||||
|
</then>
|
||||||
|
</if>
|
||||||
|
<else>
|
||||||
|
<then>
|
||||||
|
<return>
|
||||||
|
<mul>
|
||||||
|
<var name="i"/>
|
||||||
|
<call name="facc">
|
||||||
|
<sub>
|
||||||
|
<var name="i"/>
|
||||||
|
<int>1</int>
|
||||||
|
</sub>
|
||||||
|
</call>
|
||||||
|
</mul>
|
||||||
|
</return>
|
||||||
|
</then>
|
||||||
|
</else>
|
||||||
|
</switch>
|
||||||
|
</body>
|
||||||
|
</func>
|
||||||
|
|
||||||
|
<for name="i" from="1" to="10">
|
||||||
|
<body>
|
||||||
|
<output>
|
||||||
|
<call name="facc">
|
||||||
|
<var name="i"/>
|
||||||
|
</call>
|
||||||
|
</output>
|
||||||
|
</body>
|
||||||
|
</for>
|
||||||
|
</program>
|
Loading…
Reference in a new issue