initial commit

I wrote this a few year back (according to the mtime of the files
Sep 12th 2021)
This commit is contained in:
overflowerror 2024-02-02 22:01:37 +01:00
commit 6ad0168eaf
14 changed files with 1621 additions and 0 deletions

38
cmd/main.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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>