Skip to content

Commit

Permalink
[oil-language] A bunch of things about typed arrays working.
Browse files Browse the repository at this point in the history
  • Loading branch information
Andy Chu committed Sep 26, 2019
1 parent f22e0b1 commit 045a5fc
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 132 deletions.
15 changes: 8 additions & 7 deletions oil_lang/builtin_funcs.py
Expand Up @@ -41,21 +41,22 @@ def Init(mem):
#

SetGlobalFunc(mem, 'Table', objects.Table)
SetGlobalFunc(mem, 'Array', objects.ParameterizedArray())

# Types:
# TODO: Should these be Bool Int Float Str List Dict?
SetGlobalFunc(mem, 'bool', bool)
SetGlobalFunc(mem, 'int', int)
SetGlobalFunc(mem, 'Bool', bool)
SetGlobalFunc(mem, 'Int', int)

# TODO: Enable float
# OVM: PyOS_string_to_double()
# osh: Python/ovm_stub_pystrtod.c:10: PyOS_string_to_double: Assertion `0' failed.
#SetGlobalFunc(mem, 'float', float)
#SetGlobalFunc(mem, 'Float', float)

SetGlobalFunc(mem, 'tuple', tuple)
SetGlobalFunc(mem, 'str', str)
SetGlobalFunc(mem, 'list', list)
SetGlobalFunc(mem, 'dict', dict)
SetGlobalFunc(mem, 'Tuple', tuple)
SetGlobalFunc(mem, 'Str', str)
SetGlobalFunc(mem, 'List', list)
SetGlobalFunc(mem, 'Dict', dict)

SetGlobalFunc(mem, 'len', len)
SetGlobalFunc(mem, 'max', max)
Expand Down
3 changes: 3 additions & 0 deletions oil_lang/expr_eval.py
Expand Up @@ -453,6 +453,9 @@ def EvalExpr(self, node):
# TODO: Should this just be an object that ~ calls?
return objects.Regex(self.EvalRegex(node.regex))

if node.tag == expr_e.ArrayLiteral: # obj.attr
return objects.IntArray(self.EvalExpr(item) for item in node.items)

raise NotImplementedError(node.__class__.__name__)

def _EvalClassLiteralPart(self, part):
Expand Down
19 changes: 13 additions & 6 deletions oil_lang/expr_to_ast.py
Expand Up @@ -529,12 +529,7 @@ def Expr(self, pnode):
elif typ == grammar_nt.array_literal:
left_tok = children[0].tok

# Approximation for now.
tokens = [
pnode.tok for pnode in children[1:-1] if pnode.tok.id ==
Id.Lit_Chars
]
items = [expr.Const(t) for t in tokens] # type: List[expr_t]
items = [self._ArrayItem(p) for p in children[1:-1]]
return expr.ArrayLiteral(left_tok, items)

#
Expand Down Expand Up @@ -589,6 +584,18 @@ def Expr(self, pnode):
from core.meta import IdInstance
raise NotImplementedError(IdInstance(typ))

def _ArrayItem(self, p_node):
assert p_node.typ == grammar_nt.array_item

child0 = p_node.children[0]
typ0 = child0.typ
if ISNONTERMINAL(typ0):
return self.Expr(child0)
else:
if child0.tok.id == Id.Op_LParen: # (x+1)
return self.Expr(p_node.children[1])
return self.Expr(child0) # $1 ${x} etc.

def VarDecl(self, pnode):
# type: (PNode) -> command__VarDecl
"""Transform an Oil assignment statement."""
Expand Down
9 changes: 3 additions & 6 deletions oil_lang/grammar.pgen2
Expand Up @@ -147,17 +147,14 @@ word_part: Lit_Chars | Lit_Other
word: word_part*
# TODO: Change this to types and expressions, like
# @[1 2 3] @[(x) (y+1)] @[true false false]
#
# Empty:
# @Bool[] @Int[] @Float[]
# Do we need @Str[]? Or that's just @()?

array_item: (
# NOTE: Most of these occur in 'atom' above
# NOTE: Most of these occur in 'atom' above
Expr_Name | Expr_Null | Expr_True | Expr_False |
Expr_Float | Expr_DecInt | Expr_BinInt | Expr_OctInt | Expr_HexInt |
dq_string | sq_string |
sh_command_sub | braced_var_sub | simple_var_sub
sh_command_sub | braced_var_sub | simple_var_sub |
'(' test ')'
)
array_literal: (
'@[' array_item* Op_RBracket
Expand Down
21 changes: 21 additions & 0 deletions oil_lang/objects.py
Expand Up @@ -16,6 +16,27 @@
_ = log


class ParameterizedArray(object):
"""
Parameterized
For Array[Bool]
"""
def __getitem__(self, typ):
if typ is bool:
return BoolArray
if typ is int:
return IntArray
if typ is float:
return FloatArray
if typ is str:
return StrArray
raise AssertionError('typ: %s' % typ)

def __call__(self):
# Array(1 2 3)
raise AssertionError("Arrays need a parameterized type")


# These are for data frames?

class BoolArray(list):
Expand Down
76 changes: 76 additions & 0 deletions spec/oil-array.test.sh
@@ -0,0 +1,76 @@
# Typed arrays

#### integer array
var x = @[1 2 3]
echo len=$len(x)
## STDOUT:
len=3
## END

#### string array with command sub, varsub, etc.
shopt -s oil:basic

var x = 1
var a = @[$x $(echo hi)]
echo len=$len(a)
echo @a
## STDOUT:
len=2
1
hi
## END

#### arrays with expressions
shopt -s oil:basic

# Does () make makes sense?

var x = 5
var y = 6
var a = @[(x+1) (y*2)]

echo len=$len(a)
echo @a

## STDOUT:
len=2
6
12
## END

#### Empty arrays and using Array[T]
shopt -s oil:basic

var b = Array[Bool]()
var i = Array[Int]()

#var f = Array[Float]()
echo len=$len(b)
echo len=$len(i)

var b2 = Array[Bool]([true, false])
echo @b2

#echo len=$len(f)
## STDOUT:
len=0
len=0
True
False
## END


#### Arrays from generator expressions
shopt -s oil:basic

var b = Array[Bool](true for _ in 1:3)
var i = Array[Int](i+1 for i in 1:3)
var f = Array[Float](i * 2.5 for i in 1:3)
echo @b
echo @i
echo @f
## STDOUT:
len=0
len=0
len=0
## END
112 changes: 112 additions & 0 deletions spec/oil-assign.test.sh
@@ -0,0 +1,112 @@
# Test var / setvar / etc.

# TODO: GetVar needs a mode where Obj[str] gets translated to value.Str?
# Then all code will work.
#
# word_eval:
#
# val = self.mem.GetVar(var_name) ->
# val = GetWordVar(self.mem, var_name)
#
# Conversely, in oil_lang/expr_eval.py:
# LookupVar gives you a plain Python object. I don't think there's any
# downside here.
#
# repr exposes the differences.
#
# Notes:
#
# - osh/cmd_exec.py handles OilAssign, which gets wrapped in value.Obj()
# - osh/word_eval.py _ValueToPartValue handles 3 value types. Used in:
# - _EvalBracedVarSub
# - SimpleVarSub in _EvalWordPart
# - osh/expr_eval.py: _LookupVar wrapper should disallow using Oil values
# - this is legacy stuff. Both (( )) and [[ ]]
# - LhsIndexedName should not reference Oil vars either


#### integers expression and augmented assignment
var x = 1 + 2 * 3
echo x=$x

setvar x += 4
echo x=$x
## STDOUT:
x=7
x=11
## END

#### setvar when variable isn't declared results in fatal error
var x = 1
f() {
# setting global is OK
setvar x = 2
echo x=$x

setvar y = 3 # NOT DECLARED
echo y=$y
}
f
## status: 1
## STDOUT:
x=2
## END

#### var/setvar x, y = 1, 2

# Python doesn't allow you to have annotation on each variable!
# https://www.python.org/dev/peps/pep-0526/#where-annotations-aren-t-allowed
#var x Int, y Int = 3, 4
setvar x, y = 1, 2
echo $x $y
## STDOUT:
1 2
## END

#### setvar x[1] = 42
shopt -s oil:basic
var mylist = [1,2,3]
setvar x[1] = 42
echo -sep ' ' @x
## STDOUT:
1 42 3
## END


#### duplicate var def results in fatal error
var x = "global"
f() {
var x = "local"
echo x=$x
}
f
var x = "redeclaration is an error"
## status: 1
## STDOUT:
x=local
## END

#### setvar dynamic scope (TODO: change this?)
modify_with_shell_assignment() {
f=shell
}

modify_with_setvar() {
setvar f = "setvar"
}

f() {
var f = 1
echo f=$f
modify_with_shell_assignment
echo f=$f
modify_with_setvar
echo f=$f
}
f
## STDOUT:
f=1
f=shell
f=setvar
## END

0 comments on commit 045a5fc

Please sign in to comment.