we in era3 boys; overhauled expression system, and variables are now in maps
This commit is contained in:
parent
bf29e6c3dd
commit
43e450c207
14 changed files with 945 additions and 452 deletions
160
core/compiler.go
160
core/compiler.go
|
|
@ -18,6 +18,9 @@ type Compiler struct {
|
|||
source []rune
|
||||
Warnings []CompilerError
|
||||
|
||||
// optimize Whether to attempt some optimization of the emitted bytecode
|
||||
optimize bool
|
||||
|
||||
stack *Stack[LocalVariable]
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +132,7 @@ func NewCompiler(source []rune) *Compiler {
|
|||
nil,
|
||||
source,
|
||||
[]CompilerError{},
|
||||
false,
|
||||
NewStack[LocalVariable](256),
|
||||
}
|
||||
|
||||
|
|
@ -169,10 +173,14 @@ func (c *Compiler) Compile(p *Program) error {
|
|||
}
|
||||
}
|
||||
|
||||
for _, s := range p.Block.statements {
|
||||
for i, s := range p.Block.statements {
|
||||
if err := c.compile(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i != len(p.Block.statements)-1 {
|
||||
c.add(InstructionPop)
|
||||
}
|
||||
}
|
||||
|
||||
c.fileStack.Pop()
|
||||
|
|
@ -205,7 +213,7 @@ func (c *Compiler) compile(tree Node) error {
|
|||
|
||||
if len(l.items) == 0 {
|
||||
c.add(InstructionNewList)
|
||||
} else if c.isTreeConstant(l) {
|
||||
} else if c.optimize && c.isTreeConstant(l) {
|
||||
v, err := c.compute(l)
|
||||
if err != nil {
|
||||
panic(err) // this shouldn't happen
|
||||
|
|
@ -234,7 +242,7 @@ func (c *Compiler) compile(tree Node) error {
|
|||
}
|
||||
|
||||
case UnaryNodeType:
|
||||
if c.isTreeConstant(tree.(*UnaryNode).value) {
|
||||
if c.optimize && c.isTreeConstant(tree.(*UnaryNode).value) {
|
||||
v, err := c.compute(tree)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -277,12 +285,21 @@ func (c *Compiler) compile(tree Node) error {
|
|||
c.add(InstructionNil)
|
||||
|
||||
case BlockNodeType:
|
||||
if len(tree.(*BlockNode).statements) == 0 {
|
||||
c.add(InstructionNil)
|
||||
return nil
|
||||
}
|
||||
|
||||
c.addDescend()
|
||||
for _, n := range tree.(*BlockNode).statements {
|
||||
for i, n := range tree.(*BlockNode).statements {
|
||||
err := c.compile(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i != len(tree.(*BlockNode).statements)-1 {
|
||||
c.add(InstructionPop)
|
||||
}
|
||||
}
|
||||
c.addAscend()
|
||||
|
||||
|
|
@ -298,7 +315,7 @@ func (c *Compiler) compile(tree Node) error {
|
|||
return c.error(fmt.Sprintf("conditional requires boolean; cannot use non-boolean type %s", sig), n.condition)
|
||||
}
|
||||
|
||||
if c.isTreeConstant(n.condition) {
|
||||
if c.optimize && c.isTreeConstant(n.condition) {
|
||||
v, err := c.compute(n.condition)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -336,13 +353,10 @@ func (c *Compiler) compile(tree Node) error {
|
|||
}
|
||||
|
||||
// we store the position of the jump over the else code here
|
||||
var jumpOverElse Pos
|
||||
if n.otherwise != nil {
|
||||
// this would jump over the else/otherwise block in the code
|
||||
c.add(InstructionJump)
|
||||
jumpOverElse = c.ip
|
||||
c.advance(2)
|
||||
}
|
||||
// this would jump over the else/otherwise block in the code
|
||||
c.add(InstructionJump)
|
||||
jumpOverElse := c.ip
|
||||
c.advance(2)
|
||||
|
||||
// put the u16 of where to jump if the condition was false
|
||||
c.putU16(jumpByPos, uint16(c.ip-jumpByPos-2))
|
||||
|
|
@ -352,9 +366,12 @@ func (c *Compiler) compile(tree Node) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.putU16(jumpOverElse, uint16(c.ip-jumpOverElse-2))
|
||||
} else {
|
||||
c.add(InstructionNil)
|
||||
}
|
||||
|
||||
c.putU16(jumpOverElse, uint16(c.ip-jumpOverElse-2))
|
||||
|
||||
case LoopNodeType:
|
||||
n := tree.(*LoopNode)
|
||||
|
||||
|
|
@ -368,7 +385,7 @@ func (c *Compiler) compile(tree Node) error {
|
|||
}
|
||||
|
||||
alwaysLoop := false
|
||||
if c.isTreeConstant(n.condition) {
|
||||
if c.optimize && c.isTreeConstant(n.condition) {
|
||||
v, err := c.compute(n.condition)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -383,6 +400,8 @@ func (c *Compiler) compile(tree Node) error {
|
|||
}
|
||||
}
|
||||
|
||||
c.add(InstructionNil)
|
||||
|
||||
conditionPos := c.ip
|
||||
jumpValuePos := Pos(0)
|
||||
if !alwaysLoop {
|
||||
|
|
@ -396,6 +415,8 @@ func (c *Compiler) compile(tree Node) error {
|
|||
c.advance(2)
|
||||
}
|
||||
|
||||
c.add(InstructionPop)
|
||||
|
||||
err = c.compile(n.do)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -412,26 +433,47 @@ func (c *Compiler) compile(tree Node) error {
|
|||
case AssignNodeType:
|
||||
n := tree.(*AssignNode)
|
||||
|
||||
if n.name == "_" {
|
||||
// allow non-ish statements
|
||||
err := c.compile(n.value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.add(InstructionPop)
|
||||
} else {
|
||||
if n.declare && c.isVarDeclaredHere(n.name) {
|
||||
return c.error(fmt.Sprintf("%s is already declared in this scope", n.name), n)
|
||||
switch n.dest.Type() {
|
||||
case ReferenceNodeType:
|
||||
d := n.dest.(*ReferenceNode)
|
||||
|
||||
if d.name == "_" {
|
||||
return c.compile(n.value)
|
||||
}
|
||||
|
||||
err := c.addSetVar(n.name, n.value, n.declare)
|
||||
if err != nil {
|
||||
if n.declare && c.isVarDeclaredHere(d.name) {
|
||||
return c.error(fmt.Sprintf("%s is already declared in this scope", d.name), n)
|
||||
}
|
||||
|
||||
if err := c.addSetVar(d.name, n.value, n.declare); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return c.error(fmt.Sprintf("cannot assign to %s", n.dest.Type()), n.dest)
|
||||
}
|
||||
|
||||
case CallNodeType:
|
||||
n := tree.(*CallNode)
|
||||
/*
|
||||
if n.name == "_" {
|
||||
// allow non-ish statements
|
||||
err := c.compile(n.value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.add(InstructionPop)
|
||||
} else {
|
||||
if n.declare && c.isVarDeclaredHere(n.name) {
|
||||
return c.error(fmt.Sprintf("%s is already declared in this scope", n.name), n)
|
||||
}
|
||||
|
||||
err := c.addSetVar(n.name, n.value, n.declare)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
case InvokeNodeType:
|
||||
n := tree.(*InvokeNode)
|
||||
|
||||
s, err := c.deduceSignature(n.source)
|
||||
if err != nil {
|
||||
|
|
@ -443,10 +485,6 @@ func (c *Compiler) compile(tree Node) error {
|
|||
return c.error(fmt.Sprintf("cannot call non-function value of type %s", s), n)
|
||||
}
|
||||
|
||||
if !n.keep && f.Out.Type() != TypeNil {
|
||||
c.warn(fmt.Sprintf("shouldn't void result of function call (output is non-nil %s)", f.Out), n)
|
||||
}
|
||||
|
||||
if len(n.args) != len(f.In) {
|
||||
return c.error(fmt.Sprintf("wrong argument count: function of signature %s got %d, requires %d", f, len(n.args), len(f.In)), n)
|
||||
}
|
||||
|
|
@ -478,7 +516,7 @@ func (c *Compiler) compile(tree Node) error {
|
|||
return c.error(fmt.Sprintf("argument #%d does not have expected type signature: got %s, requires %s", i, sig, f.In[i]), arg)
|
||||
}
|
||||
|
||||
if c.isTreeConstant(arg) {
|
||||
if c.optimize && c.isTreeConstant(arg) {
|
||||
v, err := c.compute(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -501,10 +539,6 @@ func (c *Compiler) compile(tree Node) error {
|
|||
|
||||
c.add(InstructionCall)
|
||||
|
||||
if !n.keep {
|
||||
c.add(InstructionPop)
|
||||
}
|
||||
|
||||
case FunctionNodeType:
|
||||
n := tree.(*FunctionNode)
|
||||
|
||||
|
|
@ -588,7 +622,7 @@ func (c *Compiler) compile(tree Node) error {
|
|||
}
|
||||
|
||||
func (c *Compiler) compileBinary(binary *BinaryNode) error {
|
||||
if c.isTreeConstant(binary) {
|
||||
if c.optimize && c.isTreeConstant(binary) {
|
||||
v, err := c.compute(binary)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -825,8 +859,8 @@ func (c *Compiler) deduceSignature(tree Node) (TypeSignature, error) {
|
|||
return nil, c.error(fmt.Sprintf("cannot access property from value of type %s", sig), n)
|
||||
}
|
||||
|
||||
case CallNodeType:
|
||||
n := tree.(*CallNode)
|
||||
case InvokeNodeType:
|
||||
n := tree.(*InvokeNode)
|
||||
sig, err := c.deduceSignature(n.source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -989,29 +1023,37 @@ func (c *Compiler) affirmReturnSignature(tree Node, sig TypeSignature) error {
|
|||
case AssignNodeType:
|
||||
n := tree.(*AssignNode)
|
||||
|
||||
if !n.declare {
|
||||
prev, err := c.getVarSignature(n.name, n)
|
||||
if err != nil {
|
||||
return err
|
||||
switch n.dest.Type() {
|
||||
case ReferenceNodeType:
|
||||
name := n.dest.(*ReferenceNode).name
|
||||
|
||||
if !n.declare {
|
||||
prev, err := c.getVarSignature(name, n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sig, err := c.deduceSignature(n.value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !sig.Matches(prev) {
|
||||
return c.error(fmt.Sprintf("cannot assign value of type %s to variable %s of type %s", sig, name, prev), n.value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
sig, err := c.deduceSignature(n.value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !sig.Matches(prev) {
|
||||
return c.error(fmt.Sprintf("cannot assign value of type %s to variable %s of type %s", sig, n.name, prev), n.value)
|
||||
}
|
||||
|
||||
return nil
|
||||
c.registerVar(name, sig)
|
||||
default:
|
||||
return c.error("can neither assign nor declare to", n.dest)
|
||||
}
|
||||
|
||||
sig, err := c.deduceSignature(n.value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.registerVar(n.name, sig)
|
||||
default:
|
||||
}
|
||||
|
||||
|
|
@ -1114,13 +1156,13 @@ func (c *Compiler) isTreeConstant(tree Node) bool {
|
|||
return c.isTreeConstant(tree.(*UnaryNode).value)
|
||||
case BinaryNodeType:
|
||||
return c.isTreeConstant(tree.(*BinaryNode).Left) && c.isTreeConstant(tree.(*BinaryNode).Right)
|
||||
case CallNodeType:
|
||||
for _, arg := range tree.(*CallNode).args {
|
||||
case InvokeNodeType:
|
||||
for _, arg := range tree.(*InvokeNode).args {
|
||||
if !c.isTreeConstant(arg) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return c.isTreeConstant(tree.(*CallNode).source)
|
||||
return c.isTreeConstant(tree.(*InvokeNode).source)
|
||||
case BlockNodeType, ConditionalNodeType, LoopNodeType, AssignNodeType, FunctionNodeType,
|
||||
ReturnNodeType, AccessNodeType, BreakpointNodeType, ReferenceNodeType:
|
||||
return false
|
||||
|
|
@ -1202,7 +1244,7 @@ func (c *Compiler) compute(tree Node) (Value, error) {
|
|||
|
||||
return nil, c.error(fmt.Sprintf("unimplemented unary %s", v.Type()), n)
|
||||
|
||||
case *CallNode:
|
||||
case *InvokeNode:
|
||||
source, err := c.compute(n.source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue