we in era3 boys; overhauled expression system, and variables are now in maps

This commit is contained in:
Neemek 2026-07-09 23:10:49 +02:00
parent bf29e6c3dd
commit 43e450c207
Signed by: neemek
GPG key ID: 84FFE4D7D40AB25E
14 changed files with 945 additions and 452 deletions

View file

@ -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