summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2023-11-20 15:54:52 +0100
committerMarc Vertes <mvertes@free.fr>2023-11-20 15:54:52 +0100
commit6a0490257bf235d011004bc303306f617ac6ea31 (patch)
treee04160a26f78afaa60c4b18b3b16662d35106d97
parent001ca51323a1a54c9b3db377f0783c330666c480 (diff)
parser: add support for unary operators
-rw-r--r--lang/golang/go.go1
-rw-r--r--lang/token.go10
-rw-r--r--parser/README.md6
-rw-r--r--parser/compiler.go10
-rw-r--r--parser/expr.go15
-rw-r--r--parser/interpreter_test.go4
-rw-r--r--vm/vm.go4
7 files changed, 46 insertions, 4 deletions
diff --git a/lang/golang/go.go b/lang/golang/go.go
index 26af4f3..f5a6578 100644
--- a/lang/golang/go.go
+++ b/lang/golang/go.go
@@ -91,6 +91,7 @@ var GoSpec = &lang.Spec{
"<=": {TokenId: lang.LessEqual, Precedence: 3},
">=": {TokenId: lang.GreaterEqual, Precedence: 3},
"->": {TokenId: lang.Arrow},
+ "!": {TokenId: lang.Not},
"++": {TokenId: lang.Inc, SkipSemi: true},
"--": {TokenId: lang.Dec, SkipSemi: true},
diff --git a/lang/token.go b/lang/token.go
index a894c98..4bf9bf2 100644
--- a/lang/token.go
+++ b/lang/token.go
@@ -116,6 +116,16 @@ const (
EqualSet
)
+var UnaryOp = map[TokenId]TokenId{
+ Add: Plus, // +
+ And: Address, // &
+ Not: Not, // !
+ Mul: Deref, // *
+ Sub: Minus, // -
+ Tilde: Tilde, // ~
+ Xor: BitComp, // ^
+}
+
func (t TokenId) IsKeyword() bool { return t >= Break && t <= Var }
func (t TokenId) IsLiteral() bool { return t >= Char && t <= String }
func (t TokenId) IsOperator() bool { return t >= Add && t <= Tilde }
diff --git a/parser/README.md b/parser/README.md
index eb65dcf..118d9aa 100644
--- a/parser/README.md
+++ b/parser/README.md
@@ -34,7 +34,7 @@ Go language support:
- [ ] export to runtime
- [ ] builtin calls (new, make, copy, delete, len, cap, ...)
- [ ] out of order declarations
-- [ ] arbirtrary precision constants
+- [x] arbirtrary precision constants
- [x] basic types
- [ ] complete numeric types
- [x] function types
@@ -43,6 +43,7 @@ Go language support:
- [x] structures
- [ ] embedded structures
- [ ] recursive structures
+- [ ] literal composite objects
- [ ] interfaces
- [x] arrays, slices
- [ ] maps
@@ -70,7 +71,7 @@ Go language support:
- [x] label statement
- [ ] select statement
- [x] binary operators
-- [ ] unary operators
+- [x] unary operators
- [x] logical operators && and ||
- [ ] assign operators
- [x] operator precedence rules
@@ -78,6 +79,7 @@ Go language support:
- [x] call expressions
- [x] index expressions
- [x] selector expressions
+- [ ] slice expressions
- [ ] type convertions
- [ ] type assertions
- [ ] parametric types (generic)
diff --git a/parser/compiler.go b/parser/compiler.go
index 41448b7..bdf801b 100644
--- a/parser/compiler.go
+++ b/parser/compiler.go
@@ -77,6 +77,16 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
push(&symbol{Type: arithmeticOpType(pop(), pop())})
emit(int64(t.Pos), vm.Sub)
+ case lang.Minus:
+ emit(int64(t.Pos), vm.Push, 0)
+ emit(int64(t.Pos), vm.Sub)
+
+ case lang.Not:
+ emit(int64(t.Pos), vm.Not)
+
+ case lang.Plus:
+ // Nothing to do.
+
case lang.Index:
emit(int64(t.Pos), vm.Index)
diff --git a/parser/expr.go b/parser/expr.go
index fa8afa4..207f38b 100644
--- a/parser/expr.go
+++ b/parser/expr.go
@@ -42,10 +42,21 @@ func (p *Parser) ParseExpr(in Tokens) (out Tokens, err error) {
case lang.Int, lang.String:
out = append(out, t)
vl++
- case lang.Define, lang.Add, lang.Sub, lang.Assign, lang.Equal, lang.Greater, lang.Less, lang.Mul, lang.Land, lang.Lor, lang.Shl, lang.Shr:
+ case lang.Define, lang.Add, lang.Sub, lang.Assign, lang.Equal, lang.Greater, lang.Less, lang.Mul, lang.Land, lang.Lor, lang.Shl, lang.Shr, lang.Not:
+ if i == 0 || in[i-1].Id.IsOperator() {
+ // An operator preceded by an operator or no token is unary.
+ t.Id = lang.UnaryOp[t.Id]
+ j := len(out) - 1
+ l := out[j]
+ if p.precedence(l) > 0 {
+ out = append(out[:j], t, l)
+ break
+ }
+ out = append(out, t)
+ break
+ }
if vl < 2 {
ops = append(ops, t)
- break
}
case lang.ParenBlock:
// If the previous token is an arithmetic, logic or assign operator then
diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go
index 308474c..db9b916 100644
--- a/parser/interpreter_test.go
+++ b/parser/interpreter_test.go
@@ -65,6 +65,10 @@ func TestExpr(t *testing.T) {
{src: "7 == 2 * 3 + 1", res: "true"},
{src: "1 + 3 * 2 == 2 * 3 + 1", res: "true"},
{src: "a := 1 + 3 * 2 == 2 * 3 + 1; a", res: "true"},
+ {src: "-2", res: "-2"},
+ {src: "-2 + 5", res: "3"},
+ {src: "5 + -2", res: "3"},
+ {src: "!false", res: "true"},
})
}
diff --git a/vm/vm.go b/vm/vm.go
index fa31ff0..21205de 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -37,6 +37,7 @@ const (
Lower // n1 n2 -- cond ; cond = n1 < n2
Loweri // n1 -- cond ; cond = n1 < $1
Mul // n1 n2 -- prod ; prod = n1*n2
+ Not // c -- r ; r = !c
Pop // v --
Push // -- v
Return // [r1 .. ri] -- ; exit frame: sp = fp, fp = pop
@@ -69,6 +70,7 @@ var strop = [...]string{ // for VM tracing.
Lower: "Lower",
Loweri: "Loweri",
Mul: "Mul",
+ Not: "Not",
Pop: "Pop",
Push: "Push",
Return: "Return",
@@ -210,6 +212,8 @@ func (m *Machine) Run() (err error) {
mem = mem[:sp-1]
case Loweri:
mem[sp-1] = mem[sp-1].(int) < int(op[2])
+ case Not:
+ mem[sp-1] = !mem[sp-1].(bool)
case Pop:
mem = mem[:sp-int(op[2])]
case Push: