|
|
|
@ -73,6 +73,123 @@ func (f *Function) Compile() {
|
|
|
|
|
f.Assembler.Return()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CompileInstruction compiles a single instruction.
|
|
|
|
|
func (f *Function) CompileInstruction(line token.List) error {
|
|
|
|
|
if len(line) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if line[0].Kind == token.Keyword {
|
|
|
|
|
return f.CompileKeyword(line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expr := expression.Parse(line)
|
|
|
|
|
|
|
|
|
|
if expr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer expr.Close()
|
|
|
|
|
|
|
|
|
|
if isVariableDefinition(expr) {
|
|
|
|
|
return f.CompileVariableDefinition(expr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if isFunctionCall(expr) {
|
|
|
|
|
return f.CompileFunctionCall(expr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return errors.New(&errors.InvalidInstruction{Instruction: expr.Token.Text()}, f.File, expr.Token.Position)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CompileKeyword compiles an instruction that starts with a keyword.
|
|
|
|
|
func (f *Function) CompileKeyword(line token.List) error {
|
|
|
|
|
switch line[0].Text() {
|
|
|
|
|
case "return":
|
|
|
|
|
if len(line) > 1 {
|
|
|
|
|
value := expression.Parse(line[1:])
|
|
|
|
|
defer value.Close()
|
|
|
|
|
// TODO: Set the return value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f.Assembler.Return()
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return errors.New(&errors.KeywordNotImplemented{Keyword: line[0].Text()}, f.File, line[0].Position)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CompileVariableDefinition compiles a variable definition.
|
|
|
|
|
func (f *Function) CompileVariableDefinition(expr *expression.Expression) error {
|
|
|
|
|
if len(expr.Children) < 2 {
|
|
|
|
|
return errors.New(errors.MissingAssignValue, f.File, expr.LastChild().Token.After())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
name := expr.Children[0].Token.Text()
|
|
|
|
|
_, exists := f.Variables[name]
|
|
|
|
|
|
|
|
|
|
if exists {
|
|
|
|
|
return errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, expr.Children[0].Token.Position)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value := expr.Children[1]
|
|
|
|
|
|
|
|
|
|
// All expressions are returned to the memory pool.
|
|
|
|
|
// To avoid losing variable values, we will detach it from the expression.
|
|
|
|
|
expr.RemoveChild(value)
|
|
|
|
|
|
|
|
|
|
f.Variables[name] = &Variable{
|
|
|
|
|
Name: name,
|
|
|
|
|
Value: value,
|
|
|
|
|
IsConst: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CompileFunctionCall compiles a function call.
|
|
|
|
|
func (f *Function) CompileFunctionCall(expr *expression.Expression) error {
|
|
|
|
|
funcName := expr.Children[0].Token.Text()
|
|
|
|
|
parameters := expr.Children[1:]
|
|
|
|
|
|
|
|
|
|
for i, parameter := range parameters {
|
|
|
|
|
switch parameter.Token.Kind {
|
|
|
|
|
case token.Identifier:
|
|
|
|
|
name := parameter.Token.Text()
|
|
|
|
|
variable, exists := f.Variables[name]
|
|
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
|
panic("Unknown identifier " + name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !variable.IsConst {
|
|
|
|
|
panic("Not implemented yet")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n, _ := strconv.Atoi(variable.Value.Token.Text())
|
|
|
|
|
f.Assembler.MoveRegisterNumber(x64.SyscallArgs[i], uint64(n))
|
|
|
|
|
|
|
|
|
|
case token.Number:
|
|
|
|
|
value := parameter.Token.Text()
|
|
|
|
|
n, _ := strconv.Atoi(value)
|
|
|
|
|
f.Assembler.MoveRegisterNumber(x64.SyscallArgs[i], uint64(n))
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
panic("Unknown expression")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if funcName == "syscall" {
|
|
|
|
|
f.Assembler.Syscall()
|
|
|
|
|
} else {
|
|
|
|
|
f.Assembler.Call(funcName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PrintAsm shows the assembly instructions.
|
|
|
|
|
func (f *Function) PrintAsm() {
|
|
|
|
|
ansi.Bold.Println(f.Name)
|
|
|
|
@ -92,111 +209,17 @@ func (f *Function) PrintAsm() {
|
|
|
|
|
ansi.Dim.Println("╰────────────────────────────────────────────────────────────")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CompileInstruction compiles a single instruction.
|
|
|
|
|
func (f *Function) CompileInstruction(line token.List) error {
|
|
|
|
|
if len(line) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if line[0].Kind == token.Keyword {
|
|
|
|
|
switch line[0].Text() {
|
|
|
|
|
case "return":
|
|
|
|
|
if len(line) > 1 {
|
|
|
|
|
value := expression.Parse(line[1:])
|
|
|
|
|
defer value.Close()
|
|
|
|
|
// TODO: Set the return value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
f.Assembler.Return()
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return errors.New(&errors.KeywordNotImplemented{Keyword: line[0].Text()}, f.File, line[0].Position)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expr := expression.Parse(line)
|
|
|
|
|
|
|
|
|
|
if expr == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer expr.Close()
|
|
|
|
|
|
|
|
|
|
if expr.Token.Kind == token.Number || expr.Token.Kind == token.Identifier || expr.Token.Kind == token.String {
|
|
|
|
|
return errors.New(&errors.InvalidInstruction{Instruction: expr.Token.Text()}, f.File, expr.Token.Position)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if expr.Token.Text() == ":=" {
|
|
|
|
|
if len(expr.Children) < 2 {
|
|
|
|
|
return errors.New(errors.MissingAssignValue, f.File, expr.LastChild().Token.After())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
name := expr.Children[0].Token.Text()
|
|
|
|
|
_, exists := f.Variables[name]
|
|
|
|
|
|
|
|
|
|
if exists {
|
|
|
|
|
return errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, expr.Children[0].Token.Position)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value := expr.Children[1]
|
|
|
|
|
|
|
|
|
|
// All expressions are returned to the memory pool.
|
|
|
|
|
// To avoid losing variable values, we will remove it from the expression.
|
|
|
|
|
expr.RemoveChild(value)
|
|
|
|
|
|
|
|
|
|
f.Variables[name] = &Variable{
|
|
|
|
|
Name: name,
|
|
|
|
|
Value: value,
|
|
|
|
|
IsConst: true,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if expr.Token.Text() == "λ" {
|
|
|
|
|
funcName := expr.Children[0].Token.Text()
|
|
|
|
|
parameters := expr.Children[1:]
|
|
|
|
|
|
|
|
|
|
for i, parameter := range parameters {
|
|
|
|
|
switch parameter.Token.Kind {
|
|
|
|
|
case token.Identifier:
|
|
|
|
|
name := parameter.Token.Text()
|
|
|
|
|
variable, exists := f.Variables[name]
|
|
|
|
|
|
|
|
|
|
if !exists {
|
|
|
|
|
panic("Unknown identifier " + name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !variable.IsConst {
|
|
|
|
|
panic("Not implemented yet")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n, _ := strconv.Atoi(variable.Value.Token.Text())
|
|
|
|
|
f.Assembler.MoveRegisterNumber(x64.SyscallArgs[i], uint64(n))
|
|
|
|
|
|
|
|
|
|
case token.Number:
|
|
|
|
|
value := parameter.Token.Text()
|
|
|
|
|
n, _ := strconv.Atoi(value)
|
|
|
|
|
f.Assembler.MoveRegisterNumber(x64.SyscallArgs[i], uint64(n))
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
panic("Unknown expression")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if funcName == "syscall" {
|
|
|
|
|
f.Assembler.Syscall()
|
|
|
|
|
} else {
|
|
|
|
|
f.Assembler.Call(funcName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// String returns the function name.
|
|
|
|
|
func (f *Function) String() string {
|
|
|
|
|
return f.Name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// isVariableDefinition returns true if the expression is a variable definition.
|
|
|
|
|
func isVariableDefinition(expr *expression.Expression) bool {
|
|
|
|
|
return expr.Token.Kind == token.Operator && expr.Token.Text() == ":="
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// isFunctionCall returns true if the expression is a function call.
|
|
|
|
|
func isFunctionCall(expr *expression.Expression) bool {
|
|
|
|
|
return expr.Token.Kind == token.Operator && expr.Token.Text() == "λ"
|
|
|
|
|
}
|
|
|
|
|