lang: Allow dotted identifiers
This adds support for dotted identifiers in include statements, var expressions and function call expressions. The dotted identifiers are used to refer to classes, bind statements, and function definitions (respectively) that are included in the scope by import statements.
This commit is contained in:
@@ -149,6 +149,11 @@
|
||||
lval.str = yylex.Text()
|
||||
return DOT
|
||||
}
|
||||
/\$/ {
|
||||
yylex.pos(lval) // our pos
|
||||
lval.str = yylex.Text()
|
||||
return DOLLAR
|
||||
}
|
||||
/bool/ {
|
||||
yylex.pos(lval) // our pos
|
||||
lval.str = yylex.Text()
|
||||
|
||||
@@ -315,6 +315,216 @@ func TestLexParse0(t *testing.T) {
|
||||
//exp: ???, // FIXME: add the expected AST
|
||||
})
|
||||
}
|
||||
{
|
||||
exp := &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
&StmtBind{
|
||||
Ident: "x1",
|
||||
Value: &ExprCall{
|
||||
Name: "foo1",
|
||||
Args: []interfaces.Expr{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
values = append(values, test{
|
||||
name: "func call 1",
|
||||
code: `
|
||||
$x1 = foo1()
|
||||
`,
|
||||
fail: false,
|
||||
exp: exp,
|
||||
})
|
||||
}
|
||||
{
|
||||
exp := &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
&StmtBind{
|
||||
Ident: "x1",
|
||||
Value: &ExprCall{
|
||||
Name: "foo1",
|
||||
Args: []interfaces.Expr{
|
||||
&ExprInt{
|
||||
V: 13,
|
||||
},
|
||||
&ExprStr{
|
||||
V: "hello",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
values = append(values, test{
|
||||
name: "func call 2",
|
||||
code: `
|
||||
$x1 = foo1(13, "hello")
|
||||
`,
|
||||
fail: false,
|
||||
exp: exp,
|
||||
})
|
||||
}
|
||||
{
|
||||
exp := &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
&StmtBind{
|
||||
Ident: "x1",
|
||||
Value: &ExprCall{
|
||||
Name: "pkg.foo1",
|
||||
Args: []interfaces.Expr{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
values = append(values, test{
|
||||
name: "func call dotted 1",
|
||||
code: `
|
||||
$x1 = pkg.foo1()
|
||||
`,
|
||||
fail: false,
|
||||
exp: exp,
|
||||
})
|
||||
}
|
||||
{
|
||||
exp := &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
&StmtBind{
|
||||
Ident: "x1",
|
||||
Value: &ExprCall{
|
||||
Name: "pkg.foo1",
|
||||
Args: []interfaces.Expr{
|
||||
&ExprBool{
|
||||
V: true,
|
||||
},
|
||||
&ExprStr{
|
||||
V: "hello",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
values = append(values, test{
|
||||
name: "func call dotted 2",
|
||||
code: `
|
||||
$x1 = pkg.foo1(true, "hello")
|
||||
`,
|
||||
fail: false,
|
||||
exp: exp,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "func call dotted invalid 1",
|
||||
code: `
|
||||
$x1 = .pkg.foo1(true, "hello")
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "func call dotted invalid 2",
|
||||
code: `
|
||||
$x1 = pkg.foo1.(true, "hello")
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "func call dotted invalid 3",
|
||||
code: `
|
||||
$x1 = .pkg.foo1.(true, "hello")
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "func call dotted invalid 4",
|
||||
code: `
|
||||
$x1 = pkg..foo1(true, "hello")
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
exp := &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
&StmtBind{
|
||||
Ident: "x1",
|
||||
Value: &ExprVar{
|
||||
Name: "pkg.foo1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
values = append(values, test{
|
||||
name: "dotted var 1",
|
||||
code: `
|
||||
$x1 = $pkg.foo1
|
||||
`,
|
||||
fail: false,
|
||||
exp: exp,
|
||||
})
|
||||
}
|
||||
{
|
||||
exp := &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
&StmtBind{
|
||||
Ident: "x1",
|
||||
Value: &ExprVar{
|
||||
Name: "pkg.foo1.bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
values = append(values, test{
|
||||
name: "dotted var 2",
|
||||
code: `
|
||||
$x1 = $pkg.foo1.bar
|
||||
`,
|
||||
fail: false,
|
||||
exp: exp,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "invalid dotted var 1",
|
||||
code: `
|
||||
$x1 = $.pkg.foo1.bar
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "invalid dotted var 2",
|
||||
code: `
|
||||
$x1 = $pkg.foo1.bar.
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "invalid dotted var 3",
|
||||
code: `
|
||||
$x1 = $.pkg.foo1.bar.
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "invalid dotted var 4",
|
||||
code: `
|
||||
$x1 = $pkg..foo1.bar
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
exp := &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
@@ -903,6 +1113,158 @@ func TestLexParse0(t *testing.T) {
|
||||
exp: exp,
|
||||
})
|
||||
}
|
||||
{
|
||||
exp := &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
&StmtClass{
|
||||
Name: "c1",
|
||||
Body: &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
&StmtRes{
|
||||
Kind: "test",
|
||||
Name: &ExprStr{
|
||||
V: "t1",
|
||||
},
|
||||
Contents: []StmtResContents{
|
||||
&StmtResField{
|
||||
Field: "stringptr",
|
||||
Value: &ExprStr{
|
||||
V: "hello",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&StmtInclude{
|
||||
Name: "pkg.c1",
|
||||
},
|
||||
},
|
||||
}
|
||||
values = append(values, test{
|
||||
name: "simple dotted class 1",
|
||||
code: `
|
||||
# a dotted identifier only occurs via an imported class
|
||||
class c1 {
|
||||
test "t1" {
|
||||
stringptr => "hello",
|
||||
}
|
||||
}
|
||||
# a dotted identifier is allowed here if it's imported
|
||||
include pkg.c1
|
||||
`,
|
||||
fail: false,
|
||||
exp: exp,
|
||||
})
|
||||
}
|
||||
{
|
||||
exp := &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
&StmtClass{
|
||||
Name: "c1",
|
||||
Body: &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
&StmtRes{
|
||||
Kind: "test",
|
||||
Name: &ExprStr{
|
||||
V: "t1",
|
||||
},
|
||||
Contents: []StmtResContents{
|
||||
&StmtResField{
|
||||
Field: "stringptr",
|
||||
Value: &ExprStr{
|
||||
V: "hello",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&StmtInclude{
|
||||
Name: "pkg.ns.c1",
|
||||
},
|
||||
},
|
||||
}
|
||||
values = append(values, test{
|
||||
name: "simple dotted class 2",
|
||||
code: `
|
||||
# a dotted identifier only occurs via an imported class
|
||||
class c1 {
|
||||
test "t1" {
|
||||
stringptr => "hello",
|
||||
}
|
||||
}
|
||||
# a dotted identifier is allowed here if it's imported
|
||||
include pkg.ns.c1
|
||||
`,
|
||||
fail: false,
|
||||
exp: exp,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "simple dotted invalid class 1",
|
||||
code: `
|
||||
# a dotted identifier only occurs via an imported class
|
||||
class foo.c1 {
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "simple dotted invalid class 2",
|
||||
code: `
|
||||
# a dotted identifier only occurs via an imported class
|
||||
class foo.bar.c1 {
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "simple dotted invalid include 1",
|
||||
code: `
|
||||
class .foo.c1 {
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "simple dotted invalid include 2",
|
||||
code: `
|
||||
class foo.c1. {
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "simple dotted invalid include 3",
|
||||
code: `
|
||||
class .foo.c1. {
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
values = append(values, test{
|
||||
name: "simple dotted invalid include 4",
|
||||
code: `
|
||||
class foo..c1 {
|
||||
}
|
||||
`,
|
||||
fail: true,
|
||||
})
|
||||
}
|
||||
{
|
||||
exp := &StmtProg{
|
||||
Prog: []interfaces.Stmt{
|
||||
|
||||
@@ -79,7 +79,7 @@ func init() {
|
||||
%token OPEN_BRACK CLOSE_BRACK
|
||||
%token IF ELSE
|
||||
%token STRING BOOL INTEGER FLOAT
|
||||
%token EQUALS
|
||||
%token EQUALS DOLLAR
|
||||
%token COMMA COLON SEMICOLON
|
||||
%token ELVIS ROCKET ARROW DOT
|
||||
%token STR_IDENTIFIER BOOL_IDENTIFIER INT_IDENTIFIER FLOAT_IDENTIFIER
|
||||
@@ -211,7 +211,7 @@ stmt:
|
||||
}
|
||||
}
|
||||
// `include name`
|
||||
| INCLUDE_IDENTIFIER IDENTIFIER
|
||||
| INCLUDE_IDENTIFIER dotted_identifier
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.stmt = &StmtInclude{
|
||||
@@ -219,7 +219,7 @@ stmt:
|
||||
}
|
||||
}
|
||||
// `include name(...)`
|
||||
| INCLUDE_IDENTIFIER IDENTIFIER OPEN_PAREN call_args CLOSE_PAREN
|
||||
| INCLUDE_IDENTIFIER dotted_identifier OPEN_PAREN call_args CLOSE_PAREN
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.stmt = &StmtInclude{
|
||||
@@ -405,7 +405,7 @@ struct_field:
|
||||
}
|
||||
;
|
||||
call:
|
||||
IDENTIFIER OPEN_PAREN call_args CLOSE_PAREN
|
||||
dotted_identifier OPEN_PAREN call_args CLOSE_PAREN
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ExprCall{
|
||||
@@ -610,7 +610,10 @@ call:
|
||||
},
|
||||
}
|
||||
}
|
||||
//| VAR_IDENTIFIER OPEN_CURLY INTEGER CLOSE_CURLY
|
||||
// TODO: fix conflicts with this method, and replace the above VAR_IDENTIFIER_HX
|
||||
// TODO: allow $pkg.ns.foo{4}, instead of $foo = $pkg.ns.foo ; $foo{4}
|
||||
// TODO: use this: dotted_var_identifier OPEN_BRACK INTEGER CLOSE_BRACK instead?
|
||||
//| dotted_var_identifier OPEN_CURLY INTEGER CLOSE_CURLY
|
||||
// {
|
||||
// posLast(yylex, yyDollar) // our pos
|
||||
// $$.expr = &ExprCall{
|
||||
@@ -658,7 +661,7 @@ call_args:
|
||||
}
|
||||
;
|
||||
var:
|
||||
VAR_IDENTIFIER
|
||||
dotted_var_identifier
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ExprVar{
|
||||
@@ -952,6 +955,39 @@ type_struct_field:
|
||||
$$.str = fmt.Sprintf("%s %s", $1.str, $2.typ.String())
|
||||
}
|
||||
;
|
||||
dotted_identifier:
|
||||
IDENTIFIER
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.str = $1.str
|
||||
}
|
||||
| dotted_identifier DOT IDENTIFIER
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.str = $1.str + "." + $3.str
|
||||
}
|
||||
;
|
||||
// there are different ways the lexer/parser might choose to represent this...
|
||||
dotted_var_identifier:
|
||||
// eg: $foo (no dots)
|
||||
VAR_IDENTIFIER
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.str = $1.str
|
||||
}
|
||||
// eg: $foo . bar.baz (identifier + dotted identifier)
|
||||
| VAR_IDENTIFIER DOT dotted_identifier
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.str = $1.str + "." + $3.str
|
||||
}
|
||||
// eg: $ foo.bar.baz (dollar prefix + dotted identifier)
|
||||
| DOLLAR dotted_identifier
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.str = $2.str
|
||||
}
|
||||
;
|
||||
%%
|
||||
// pos is a helper function used to track the position in the parser.
|
||||
func pos(y yyLexer, dollar yySymType) {
|
||||
|
||||
Reference in New Issue
Block a user