diff options
| author | Marc Vertes <mvertes@free.fr> | 2026-01-06 19:02:29 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2026-01-06 19:02:29 +0100 |
| commit | bffc031ea83c7176aac3d3828de0060c6630140c (patch) | |
| tree | 32e30f3bec94884936c2cfc2d53d3ae496e13d61 /lang | |
| parent | f07fc0178831432b68f1b9bd6c96b257aa2e9abe (diff) | |
fix: correct and simplify parsing of expressions.
The expressions were parsed from right to left, and it was incorrect and
cumbersome. Now they are processed from left to right, with a simpler
and correct handling of precedence rules.
The vm function call syntax has been changed to set the function
before the input arguments on the stack, as to follow the declaring
order in languages.
Diffstat (limited to 'lang')
| -rw-r--r-- | lang/golang/go.go | 180 | ||||
| -rw-r--r-- | lang/spec.go | 28 | ||||
| -rw-r--r-- | lang/token.go | 3 |
3 files changed, 134 insertions, 77 deletions
diff --git a/lang/golang/go.go b/lang/golang/go.go index 47baee2..603b256 100644 --- a/lang/golang/go.go +++ b/lang/golang/go.go @@ -53,75 +53,115 @@ var GoSpec = &lang.Spec{ "/*": lang.CharStr, "//": lang.CharStr | lang.ExcludeEnd | lang.EosValidEnd, }, - TokenProps: map[string]lang.TokenProp{ - // Block tokens (can be nested) - "{..}": {Token: lang.BraceBlock}, - "[..]": {Token: lang.BracketBlock}, - "(..)": {Token: lang.ParenBlock}, - - // String tokens (not nested) - "//..": {Token: lang.Comment}, - "/*..": {Token: lang.Comment}, - `".."`: {Token: lang.String}, - "`..`": {Token: lang.String}, - - // Separators - ",": {Token: lang.Comma}, - ";": {Token: lang.Semicolon}, - ".": {Token: lang.Period}, - ":": {Token: lang.Colon}, - - // Operators - "&": {Token: lang.And, Precedence: 1}, - "*": {Token: lang.Mul, Precedence: 1}, - "/": {Token: lang.Quo, Precedence: 1}, - "%": {Token: lang.Rem, Precedence: 1}, - "<<": {Token: lang.Shl, Precedence: 1}, - ">>": {Token: lang.Shr, Precedence: 1}, - "+": {Token: lang.Add, Precedence: 2}, - "-": {Token: lang.Sub, Precedence: 2}, - "=": {Token: lang.Assign, Precedence: 6}, - "+=": {Token: lang.AddAssign, Precedence: 6}, - "<": {Token: lang.Less, Precedence: 3}, - ">": {Token: lang.Greater, Precedence: 3}, - "^": {Token: lang.Xor, Precedence: 2}, - "~": {Token: lang.Tilde}, - "&&": {Token: lang.Land, Precedence: 4}, - "||": {Token: lang.Lor, Precedence: 5}, - ":=": {Token: lang.Define, Precedence: 6}, - "==": {Token: lang.Equal, Precedence: 3}, - "<=": {Token: lang.LessEqual, Precedence: 3}, - ">=": {Token: lang.GreaterEqual, Precedence: 3}, - "->": {Token: lang.Arrow}, - "!": {Token: lang.Not}, - "++": {Token: lang.Inc, SkipSemi: true}, - "--": {Token: lang.Dec, SkipSemi: true}, - - // Reserved keywords - "break": {Token: lang.Break}, - "case": {Token: lang.Case, SkipSemi: true}, - "chan": {Token: lang.Chan, SkipSemi: true}, - "const": {Token: lang.Const, SkipSemi: true}, - "continue": {Token: lang.Continue}, - "default": {Token: lang.Case, SkipSemi: true}, - "defer": {Token: lang.Defer, SkipSemi: true}, - "else": {Token: lang.Else, SkipSemi: true}, - "fallthrough": {Token: lang.Fallthrough}, - "for": {Token: lang.For, SkipSemi: true}, - "func": {Token: lang.Func, SkipSemi: true}, - "go": {Token: lang.Go, SkipSemi: true}, - "goto": {Token: lang.Goto, SkipSemi: true}, - "if": {Token: lang.If, SkipSemi: true}, - "import": {Token: lang.Import, SkipSemi: true}, - "interface": {Token: lang.Interface, SkipSemi: true}, - "map": {Token: lang.Map, SkipSemi: true}, - "package": {Token: lang.Package, SkipSemi: true}, - "range": {Token: lang.Range, SkipSemi: true}, - "return": {Token: lang.Return}, - "select": {Token: lang.Select, SkipSemi: true}, - "struct": {Token: lang.Struct, SkipSemi: true}, - "switch": {Token: lang.Switch, SkipSemi: true}, - "type": {Token: lang.Type, SkipSemi: true}, - "var": {Token: lang.Var, SkipSemi: true}, + Tokens: map[string]lang.Token{ + "{..}": lang.BraceBlock, + "[..]": lang.BracketBlock, + "(..)": lang.ParenBlock, + "//..": lang.Comment, + "/*..": lang.Comment, + `".."`: lang.String, + "`..`": lang.String, + ",": lang.Comma, + ";": lang.Semicolon, + ".": lang.Period, + ":": lang.Colon, + "&": lang.And, + "*": lang.Mul, + "/": lang.Quo, + "%": lang.Rem, + "<<": lang.Shl, + ">>": lang.Shr, + "+": lang.Add, + "-": lang.Sub, + "=": lang.Assign, + "+=": lang.AddAssign, + "<": lang.Less, + ">": lang.Greater, + "^": lang.Xor, + "~": lang.Tilde, + "&&": lang.Land, + "||": lang.Lor, + ":=": lang.Define, + "==": lang.Equal, + "<=": lang.LessEqual, + ">=": lang.GreaterEqual, + "->": lang.Arrow, + "!": lang.Not, + "++": lang.Inc, + "--": lang.Dec, + "break": lang.Break, + "case": lang.Case, + "chan": lang.Chan, + "const": lang.Const, + "continue": lang.Continue, + "default": lang.Case, // Consider "default" as an empty "case" clause. + "defer": lang.Defer, + "else": lang.Else, + "fallthrough": lang.Fallthrough, + "for": lang.For, + "func": lang.Func, + "go": lang.Go, + "goto": lang.Goto, + "if": lang.If, + "import": lang.Import, + "interface": lang.Interface, + "map": lang.Map, + "package": lang.Package, + "range": lang.Range, + "return": lang.Return, + "select": lang.Select, + "struct": lang.Struct, + "switch": lang.Switch, + "type": lang.Type, + "var": lang.Var, + }, + TokenProps: []lang.TokenProp{ + lang.And: {Precedence: 5}, + lang.Mul: {Precedence: 5}, + lang.Quo: {Precedence: 5}, + lang.Rem: {Precedence: 5}, + lang.Shl: {Precedence: 5}, + lang.Shr: {Precedence: 5}, + lang.Add: {Precedence: 4}, + lang.Sub: {Precedence: 4}, + lang.Xor: {Precedence: 4}, + lang.Or: {Precedence: 4}, + lang.Equal: {Precedence: 3}, + lang.LessEqual: {Precedence: 3}, + lang.GreaterEqual: {Precedence: 3}, + lang.Less: {Precedence: 3}, + lang.Greater: {Precedence: 3}, + lang.Land: {Precedence: 1}, + lang.Lor: {Precedence: 0}, + lang.Minus: {Precedence: 6}, + lang.Not: {Precedence: 6}, + lang.Call: {Precedence: 6}, + lang.Index: {Precedence: 6}, + lang.Period: {Precedence: 7}, + lang.Colon: {Precedence: 7}, + lang.Inc: {SkipSemi: true}, + lang.Dec: {SkipSemi: true}, + lang.Case: {SkipSemi: true}, + lang.Chan: {SkipSemi: true}, + lang.Const: {SkipSemi: true}, + lang.Default: {SkipSemi: true}, + lang.Defer: {SkipSemi: true}, + lang.Else: {SkipSemi: true}, + lang.For: {SkipSemi: true}, + lang.Func: {SkipSemi: true}, + lang.Go: {SkipSemi: true}, + lang.Goto: {SkipSemi: true}, + lang.If: {SkipSemi: true}, + lang.Import: {SkipSemi: true}, + lang.Interface: {SkipSemi: true}, + lang.Map: {SkipSemi: true}, + lang.Package: {SkipSemi: true}, + lang.Range: {SkipSemi: true}, + lang.Select: {SkipSemi: true}, + lang.Struct: {SkipSemi: true}, + lang.Switch: {SkipSemi: true}, + lang.Type: {SkipSemi: true}, + lang.Var: {SkipSemi: true}, + lang.MaxTok: {}, // To ensure that all Tokens have a TokenProp. }, } diff --git a/lang/spec.go b/lang/spec.go index b1b2580..37017e7 100644 --- a/lang/spec.go +++ b/lang/spec.go @@ -21,22 +21,36 @@ const ( // ASCIILen is the length of the ASCII characters set. const ASCIILen = 1 << 7 // 128 +// Associativity represent the associativity rule of an operator. +type Associativity int + +// Associativity kinds for operators. +const ( + Aboth Associativity = iota // both left and right associative + Aleft // left associative only + Aright // right associative only + Anon // non associative +) + // TokenProp represent token properties for parsing. type TokenProp struct { Token SkipSemi bool // automatic semicolon insertion after newline Precedence int // operator precedence + Associativity } // Spec represents the language specification for scanning. type Spec struct { - CharProp [ASCIILen]uint // special Character properties - End map[string]string // end delimiters, indexed by start - BlockProp map[string]uint // block properties - TokenProps map[string]TokenProp // token properties - DotNum bool // true if a number can start with '.' - IdentASCII bool // true if an identifier can be in ASCII only - NumUnder bool // true if a number can contain _ character + CharProp [ASCIILen]uint // special Character properties + End map[string]string // end delimiters, indexed by start + BlockProp map[string]uint // block properties + Tokens map[string]Token // token per string + TokenProps []TokenProp // token properties, indexed by token + DotNum bool // true if a number can start with '.' + IdentASCII bool // true if an identifier can be in ASCII only + NumUnder bool // true if a number can contain _ character + // TokenProps map[string]TokenProp // token properties } // HasInit stores if a statement may contain a simple init statement. diff --git a/lang/token.go b/lang/token.go index 41c8439..4f5da35 100644 --- a/lang/token.go +++ b/lang/token.go @@ -120,6 +120,9 @@ const ( JumpSetTrue Label New + + // This must be the last token value. + MaxTok ) // UnaryOp contains the set of unary operators. |
