Skip to content

Commit

Permalink
keyword: replace cfunction with extern
Browse files Browse the repository at this point in the history
  • Loading branch information
douyixuan committed Jul 15, 2024
1 parent 5e96760 commit 89e2684
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 196 deletions.
4 changes: 4 additions & 0 deletions compiler/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ func (c *Compiler) compile(instructions []parser.Node) {
c.compileBreakNode(v)
case *parser.ContinueNode:
c.compileContinueNode(v)
case *parser.ExternNode:
for _, fn := range v.FuncNodes {
c.compileDefineFuncNode(fn)
}

case *parser.DeclarePackageNode:
// TODO: Make use of it
Expand Down
8 changes: 4 additions & 4 deletions compiler/compiler/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (c *Compiler) funcType(params, returnTypes []parser.TypeNode) (retType type
// ABI description
// method: package + _method_ + type + _ + name
// named variable: package + name
// cffi: function_name
// cffi(extern): function_name
// lambda: package + anonName
func (c *Compiler) compileDefineFuncNode(v *parser.DefineFuncNode) value.Value {
var compiledName string
Expand All @@ -102,7 +102,7 @@ func (c *Compiler) compileDefineFuncNode(v *parser.DefineFuncNode) value.Value {
compiledName = c.currentPackageName + "_method_" + v.MethodOnType.TypeName + "_" + v.Name
} else if v.IsNamed {
// todo ffi set identifier
if c.currentPackageName == "tx" {
if v.IsExtern {
compiledName = v.Name
} else {
compiledName = c.currentPackageName + "_" + v.Name
Expand Down Expand Up @@ -141,7 +141,7 @@ func (c *Compiler) compileDefineFuncNode(v *parser.DefineFuncNode) value.Value {
} else {
fn = c.module.NewFunc(compiledName, funcRetType.LLVM(), llvmParams...)
// register ffi function definnition for tx package and os package
if v.IsCFunc {
if v.IsExtern {
// do not generate block
} else {
entry = fn.NewBlock(name.Block())
Expand All @@ -158,7 +158,7 @@ func (c *Compiler) compileDefineFuncNode(v *parser.DefineFuncNode) value.Value {

// register ffi function definition for tx package
// without generate func body
if v.IsCFunc {
if v.IsExtern {
val := value.Value{
Type: typesFunc,
Value: fn,
Expand Down
2 changes: 1 addition & 1 deletion compiler/lexer/keywords.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var keywords = map[string]struct{}{
"if": {},
"else": {},
"func": {},
"cfunction": {},
"extern": {},
"return": {},
"type": {},
"table": {},
Expand Down
224 changes: 224 additions & 0 deletions compiler/parser/func.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package parser

import (
"fmt"
"strings"

"github.com/cell-labs/cell-script/compiler/lexer"
)

// DefineFuncNode creates a new named function
type DefineFuncNode struct {
baseNode

Name string
IsNamed bool

IsMethod bool
IsExtern bool

MethodOnType *SingleTypeNode
IsPointerReceiver bool
InstanceName string

Arguments []*NameNode
ReturnValues []*NameNode
Body []Node
}

func (dfn DefineFuncNode) String() string {
var body []string

for _, b := range dfn.Body {
body = append(body, fmt.Sprintf("%+v", b))
}

if dfn.IsMethod {
return fmt.Sprintf("func+m (%+v) %s(%+v) %+v {\n\t%s\n}", dfn.InstanceName, dfn.Name, dfn.Arguments, dfn.ReturnValues, strings.Join(body, "\n\t"))
} else if dfn.IsNamed {
return fmt.Sprintf("func+n %s(%+v) %+v {\n\t%s\n}", dfn.Name, dfn.Arguments, dfn.ReturnValues, strings.Join(body, "\n\t"))
} else {
return fmt.Sprintf("func+v (%+v) %+v {\n\t%s\n}", dfn.Arguments, dfn.ReturnValues, strings.Join(body, "\n\t"))
}
}

type ExternNode struct {
baseNode
FuncNodes []*DefineFuncNode
}

func (en ExternNode) String() string {
var fns []string

for _, f := range en.FuncNodes {
fns = append(fns, fmt.Sprintf("%+v", f.String()))
}

return fmt.Sprintf("extern {\n\t%s\n}", strings.Join(fns, "\n\t"))
}

func (p *parser) parseExtern() *ExternNode {
// Single extern statement
expectFuncString := p.lookAhead(0)
if expectFuncString.Type == lexer.KEYWORD && expectFuncString.Val == "func" {
p.i++
dfn := p.parseFuncDefinition()
dfn.IsExtern = true
return &ExternNode{
FuncNodes: []*DefineFuncNode{dfn},
}
}

fns := []*DefineFuncNode{}
// Multiple extern
p.expect(lexer.Item{Type: lexer.OPERATOR, Val: "("}, p.lookAhead(0))
p.i++
for {
checkIfEndParen := p.lookAhead(0)
if checkIfEndParen.Type == lexer.OPERATOR && checkIfEndParen.Val == ")" {
break
}
if checkIfEndParen.Type == lexer.EOL {
p.i++
continue
}

if checkIfEndParen.Type == lexer.KEYWORD && checkIfEndParen.Val == "func" {
p.i++
fn := p.parseFuncDefinition()
fn.IsExtern = true
fns = append(fns, fn)
continue
}

panic(fmt.Sprintf("Failed to parse extern: %+v", checkIfEndParen))
}
return &ExternNode{
FuncNodes: fns,
}
}

// The tokens after keyword "func"
func (p *parser) parseFuncDefinition() *DefineFuncNode {
defineFunc := &DefineFuncNode{}
var argsOrMethodType []*NameNode
var canBeMethod bool

checkIfOpeningParen := p.lookAhead(0)
if checkIfOpeningParen.Type == lexer.OPERATOR && checkIfOpeningParen.Val == "(" {
p.i++
argsOrMethodType = p.parseFunctionArguments()
canBeMethod = true
}

checkIfIdentifier := p.lookAhead(0)
checkIfOpeningParen = p.lookAhead(1)

if canBeMethod && checkIfIdentifier.Type == lexer.IDENTIFIER &&
checkIfOpeningParen.Type == lexer.OPERATOR && checkIfOpeningParen.Val == "(" {

defineFunc.IsMethod = true
defineFunc.IsNamed = true
defineFunc.Name = checkIfIdentifier.Val

if len(argsOrMethodType) != 1 {
panic("Unexpected count of types in method")
}

defineFunc.InstanceName = argsOrMethodType[0].Name

methodOnType := argsOrMethodType[0].Type

if pointerSingleTypeNode, ok := methodOnType.(*PointerTypeNode); ok {
defineFunc.IsPointerReceiver = true
methodOnType = pointerSingleTypeNode.ValueType
}

if singleTypeNode, ok := methodOnType.(*SingleTypeNode); ok {
defineFunc.MethodOnType = singleTypeNode
} else {
panic(fmt.Sprintf("could not find type in method defitition: %T", methodOnType))
}
}

name := p.lookAhead(0)
openParen := p.lookAhead(1)
if name.Type == lexer.IDENTIFIER && openParen.Type == lexer.OPERATOR && openParen.Val == "(" {
defineFunc.Name = name.Val
defineFunc.IsNamed = true

p.i++
p.i++

// Parse argument list
defineFunc.Arguments = p.parseFunctionArguments()
} else {
defineFunc.Arguments = argsOrMethodType
}

// Parse return types
var retTypesNodeNames []*NameNode

checkIfOpeningCurly := p.lookAhead(0)
if checkIfOpeningCurly.Type != lexer.OPERATOR || checkIfOpeningCurly.Val != "{" {

// Check if next is an opening parenthesis
// Is optional if there's only one return type, is required
// if there is multiple ones
var allowMultiRetVals bool

checkIfOpenParen := p.lookAhead(0)
if checkIfOpenParen.Type == lexer.OPERATOR && checkIfOpenParen.Val == "(" {
allowMultiRetVals = true
p.i++
}

for {
nameNode := &NameNode{}

// Support both named return values and when we only get the type
retTypeOrNamed, err := p.parseOneType()
if err != nil {
panic(err)
}
p.i++

// Next can be type, that means that the previous was the name of the var
isType := p.lookAhead(0)
if isType.Type == lexer.IDENTIFIER {
retType, err := p.parseOneType()
if err != nil {
panic(err)
}
p.i++

nameNode.Name = retTypeOrNamed.Type()
nameNode.Type = retType
} else {
nameNode.Type = retTypeOrNamed
}

retTypesNodeNames = append(retTypesNodeNames, nameNode)

if !allowMultiRetVals {
break
}

// Check if comma or end parenthesis
commaOrEnd := p.lookAhead(0)
if commaOrEnd.Type == lexer.OPERATOR && commaOrEnd.Val == "," {
p.i++
continue
}

if commaOrEnd.Type == lexer.OPERATOR && commaOrEnd.Val == ")" {
p.i++
break
}
panic("Could not parse function return types")
}
}

defineFunc.ReturnValues = retTypesNodeNames
return defineFunc
}
35 changes: 0 additions & 35 deletions compiler/parser/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,41 +180,6 @@ func (ConditionNode) String() string {
return "condition"
}

// DefineFuncNode creates a new named function
type DefineFuncNode struct {
baseNode

Name string
IsNamed bool

IsMethod bool
IsCFunc bool

MethodOnType *SingleTypeNode
IsPointerReceiver bool
InstanceName string

Arguments []*NameNode
ReturnValues []*NameNode
Body []Node
}

func (dfn DefineFuncNode) String() string {
var body []string

for _, b := range dfn.Body {
body = append(body, fmt.Sprintf("%+v", b))
}

if dfn.IsMethod {
return fmt.Sprintf("func+m (%+v) %s(%+v) %+v {\n\t%s\n}", dfn.InstanceName, dfn.Name, dfn.Arguments, dfn.ReturnValues, strings.Join(body, "\n\t"))
} else if dfn.IsNamed {
return fmt.Sprintf("func+n %s(%+v) %+v {\n\t%s\n}", dfn.Name, dfn.Arguments, dfn.ReturnValues, strings.Join(body, "\n\t"))
} else {
return fmt.Sprintf("func+v (%+v) %+v {\n\t%s\n}", dfn.Arguments, dfn.ReturnValues, strings.Join(body, "\n\t"))
}
}

// NameNode retreives a named variable
type NameNode struct {
baseNode
Expand Down
Loading

0 comments on commit 89e2684

Please sign in to comment.