mirror of
https://github.com/sigmasternchen/xml-programming
synced 2025-03-14 23:58:58 +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