diff --git a/core/id_kind.py b/core/id_kind.py index 47f06ce0e5..abddf8a266 100755 --- a/core/id_kind.py +++ b/core/id_kind.py @@ -384,11 +384,15 @@ def AddKinds(spec): 'NotTilde', # !~ + # TODO: We could use the same Dummy for all? 'WordsDummy', # Used for @() (words in lex_mode_e.ShCommand) 'CommandDummy', # Used for $() (command in lex_mode_e.ShCommand) 'DqDummy', # Used for "" 'SqDummy', # Used for '' + # Constants + 'Null', 'True', 'False', + # Keywords are resolved after lexing, but otherwise behave like tokens. # NOTE: These are not used because pgen2 automatically creates a # gr.keywords dict in the grammar. diff --git a/frontend/lex.py b/frontend/lex.py index f15c994cbb..c7559a7c2e 100644 --- a/frontend/lex.py +++ b/frontend/lex.py @@ -822,6 +822,12 @@ def IsKeyword(name): # NOTE: Borrowing tokens from Arith (i.e. $(( )) ), but not using LexerPairs(). LEXER_DEF[lex_mode_e.Expr] = _OIL_LEFT_SUBS + _OIL_LEFT_UNQUOTED + [ + # NOTE: pgen2 is taking care of 'in', 'is', etc.? Should we register those? + # We probably want those too. + C('null', Id.Expr_Null), + C('true', Id.Expr_True), + C('false', Id.Expr_False), + # These can be looked up as keywords separately, so you enforce that they have # space around them? R(VAR_NAME_RE, Id.Expr_Name), diff --git a/oil_lang/expr_eval.py b/oil_lang/expr_eval.py index 73e915bfca..fe43b161c7 100644 --- a/oil_lang/expr_eval.py +++ b/oil_lang/expr_eval.py @@ -93,7 +93,15 @@ def EvalExpr(self, node): print('') if node.tag == expr_e.Const: - return int(node.c.val) + id_ = node.c.id + if id_ == Id.Expr_Digits: + return int(node.c.val) + elif id_ == Id.Expr_Null: + return None + elif id_ == Id.Expr_True: + return True + elif id_ == Id.Expr_False: + return False if node.tag == expr_e.Var: return self.LookupVar(node.name.val) diff --git a/oil_lang/expr_to_ast.py b/oil_lang/expr_to_ast.py index d995943aa7..a92972f079 100644 --- a/oil_lang/expr_to_ast.py +++ b/oil_lang/expr_to_ast.py @@ -316,8 +316,11 @@ def Expr(self, pnode): if tok.id == Id.Expr_Name: return expr.Var(tok) + # TODO: Should I combine all of these? elif tok.id == Id.Expr_Digits: return expr.Const(tok) + elif tok.id in (Id.Expr_Null, Id.Expr_True, Id.Expr_False): + return expr.Const(tok) else: raise AssertionError(tok.id) diff --git a/oil_lang/grammar.pgen2 b/oil_lang/grammar.pgen2 index 4e2fc369dd..cfad474243 100644 --- a/oil_lang/grammar.pgen2 +++ b/oil_lang/grammar.pgen2 @@ -113,7 +113,7 @@ atom_expr: ['await'] atom trailer* atom: ('(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '{' [dictorsetmaker] '}' | - NAME | NUMBER | '...' | 'None' | 'True' | 'False' | + NAME | NUMBER | '...' | Expr_Null | Expr_True | Expr_False | array_literal | sh_array_literal | command_sub | sh_command_sub | regex_literal | diff --git a/spec/oil-expr.test.sh b/spec/oil-expr.test.sh index 8bf694befc..3710cf3a93 100644 --- a/spec/oil-expr.test.sh +++ b/spec/oil-expr.test.sh @@ -367,3 +367,29 @@ bar foo bar ## END + +#### null / true / false +shopt -s all:oil +var n = null +if (n) { + echo yes +} else { + echo no +} +var t = true +if (t) { + echo yes +} else { + echo no +} +var f = false +if (f) { + echo yes +} else { + echo no +} +## STDOUT: +no +yes +no +## END