Author: coke
Date: Wed Sep 28 16:52:37 2005
New Revision: 9269

Modified:
   trunk/MANIFEST
   trunk/config/gen/makefiles/tcl.in
   trunk/languages/tcl/TODO
   trunk/languages/tcl/docs/hacks.pod
   trunk/languages/tcl/lib/commands/expr.pir
   trunk/languages/tcl/lib/commands/for.pir
   trunk/languages/tcl/lib/commands/if.pir
   trunk/languages/tcl/lib/commands/while.pir
   trunk/languages/tcl/lib/expression.pir
   trunk/languages/tcl/lib/parser.pir
   trunk/languages/tcl/lib/tclcommand.pir
   trunk/languages/tcl/lib/tclconst.pir
   trunk/languages/tcl/lib/tclfunc.pir
   trunk/languages/tcl/lib/tclops.pir
   trunk/languages/tcl/lib/tclvar.pir
   trunk/languages/tcl/t/cmd_expr.t
   trunk/languages/tcl/t/tcl_misc.t
   trunk/languages/tcl/tcl.pir_template
   trunk/languages/tcl/tcl.pl
Log:
Convert tcl's [expr] to be a compiler.

Now, to evaluate an expression, first call expression_parse(), which returns an 
executable .Sub.
to get the value of the expression at a later time, simply invoke that sub.

Remove unecessary "interpret" code from various lib classes. (Adding "compile" 
methods). Additionally,
remove the AST that was used by exp_parse to hand to exp_compile - now compile 
is handed a single
object which has as attributes the appropriate objects.

All tests pass.



Modified: trunk/MANIFEST
==============================================================================
--- trunk/MANIFEST      (original)
+++ trunk/MANIFEST      Wed Sep 28 16:52:37 2005
@@ -1458,6 +1458,7 @@ languages/tcl/lib/parser.pir            
 languages/tcl/lib/returncodes.pir                 [tcl]
 languages/tcl/lib/string_to_list.pir              [tcl]
 languages/tcl/lib/string.pir                      [tcl]
+languages/tcl/lib/tclbinaryops.pir                [tcl]
 languages/tcl/lib/tclcommand.pir                  [tcl]
 languages/tcl/lib/tclcommandlist.pir              [tcl]
 languages/tcl/lib/tclconst.pir                    [tcl]

Modified: trunk/config/gen/makefiles/tcl.in
==============================================================================
--- trunk/config/gen/makefiles/tcl.in   (original)
+++ trunk/config/gen/makefiles/tcl.in   Wed Sep 28 16:52:37 2005
@@ -77,7 +77,7 @@ lib${slash}variables.pir \
 tcl.pir_template \
 tcl.pl
 
-tcl.pbc: pmcs lib${slash}tcllib.pbc lib${slash}tclcommand.pbc 
lib${slash}tclcommandlist.pbc lib${slash}tclconst.pbc lib${slash}tclfunc.pbc 
lib${slash}tclops.pbc lib${slash}tclvar.pbc lib${slash}tclword.pbc tcl.pir
+tcl.pbc: pmcs lib${slash}tcllib.pbc lib${slash}tclbinaryops.pbc 
lib${slash}tclcommand.pbc lib${slash}tclcommandlist.pbc lib${slash}tclconst.pbc 
lib${slash}tclfunc.pbc lib${slash}tclops.pbc lib${slash}tclvar.pbc 
lib${slash}tclword.pbc tcl.pir
        $(PARROT) --output=tcl.pbc tcl.pir
 
 pmcs:
@@ -95,6 +95,9 @@ lib${slash}tcllib.pir: $(DEPS)
 lib${slash}tcllib.pbc: lib${slash}tcllib.pir
        $(PARROT) --output=$(LIBPATH)${slash}tcllib.pbc 
$(LIBPATH)${slash}tcllib.pir
 
+lib${slash}tclbinaryops.pbc: lib${slash}tclbinaryops.pir
+       $(PARROT) --output=$(LIBPATH)${slash}tclbinaryops.pbc 
$(LIBPATH)${slash}tclbinaryops.pir
+
 lib${slash}tclcommand.pbc: lib${slash}tclcommand.pir
        $(PARROT) --output=$(LIBPATH)${slash}tclcommand.pbc 
$(LIBPATH)${slash}tclcommand.pir
 

Modified: trunk/languages/tcl/TODO
==============================================================================
--- trunk/languages/tcl/TODO    (original)
+++ trunk/languages/tcl/TODO    Wed Sep 28 16:52:37 2005
@@ -61,18 +61,8 @@ Perhaps via [namespace import]? Can fake
 
 =item [expr]
 
-the expr AST could use a rework. Be nice if it just passed around something
-invokable.
-
-clean up expr so that it's not passing around
-type indicators. - just use PMCs everywhere. (Though we may still need
-to have CONST vs. VARIABLE types.)
-
-The type indicator is also used to disambiguate operators and function calls
-from operands. We can easily remove it for operands, but how to cleanly
-remove this for operators?
-
-TBD: blocks and strings as operands
+Add "is_const" global which checks to see if something is, in fact,
+constant. (which will allow us to then constant fold)
 
 TBD: ternary op
 

Modified: trunk/languages/tcl/docs/hacks.pod
==============================================================================
--- trunk/languages/tcl/docs/hacks.pod  (original)
+++ trunk/languages/tcl/docs/hacks.pod  Wed Sep 28 16:52:37 2005
@@ -80,8 +80,6 @@ actual commands and variables. I think t
 and, if any other languages support this feature, this would give us a chance
 to interoperate.
 
-=back
-
 =item list splicing
 
 There are several cases where we convert TclLists or ResizablePMCArrays to
@@ -89,6 +87,16 @@ Arrays so that we can use the splice opc
 support in parrot array classes, as well as our own. 
 L<lib/commands/linsert.pir>
 
+=item marshalling/unmarshalling
+
+When compiling, there is currently no easy way to embed a PMC that exists
+at runtime into the compiled code. Because of this, we have to interrogate 
+the PMC at compile time for its type, and then handroll the appropriate PIR
+to intialize it's value. We should be able to freeze the PMC during 
compilation,
+And then have in the generated code C<$P1 = thaw "...">.
+
+=back
+
 =cut
 
 

Modified: trunk/languages/tcl/lib/commands/expr.pir
==============================================================================
--- trunk/languages/tcl/lib/commands/expr.pir   (original)
+++ trunk/languages/tcl/lib/commands/expr.pir   Wed Sep 28 16:52:37 2005
@@ -17,7 +17,6 @@
   .local pmc expression_p
   .local pmc expression_i
   expression_p = find_global "_Tcl", "__expression_parse"
-  expression_i = find_global "_Tcl", "__expression_interpret"
 
   expr = ""
   looper = 0
@@ -35,8 +34,8 @@ loop:
   goto loop
 
 loop_done:
-  retval = expression_p(expr)
-  .return expression_i(retval) 
+  $P1 = expression_p(expr)
+  .return $P1()
 
 no_args:
   .throw("wrong # args: should be \"expr arg ?arg ...?\"")

Modified: trunk/languages/tcl/lib/commands/for.pir
==============================================================================
--- trunk/languages/tcl/lib/commands/for.pir    (original)
+++ trunk/languages/tcl/lib/commands/for.pir    Wed Sep 28 16:52:37 2005
@@ -17,14 +17,13 @@
   next_code  = argv[2]
   body_code  = argv[3]
 
-  .local pmc    start_parsed, next_parsed, body_parsed, test_parsed
+  .local pmc    start_parsed, next_parsed, body_parsed, test_compiled
   .local pmc retval
 
   .local pmc parse
-  .local pmc expression_p, expression_i
+  .local pmc expression_p
   parse = find_global "_Tcl", "parse"
   expression_p = find_global "_Tcl", "__expression_parse"
-  expression_i = find_global "_Tcl", "__expression_interpret"
 
   # Parse the bits that are code.
   start_parsed = parse(start_code)
@@ -44,8 +43,8 @@ continue:
   # then execute next
   next_parsed."interpret"()
   # then check condition
-  test_parsed = expression_p(test_code)
-  retval = expression_i(test_parsed)
+  test_compiled = expression_p(test_code)
+  retval = test_compiled()
   if retval goto for_loop
 
   .return ("") 

Modified: trunk/languages/tcl/lib/commands/if.pir
==============================================================================
--- trunk/languages/tcl/lib/commands/if.pir     (original)
+++ trunk/languages/tcl/lib/commands/if.pir     Wed Sep 28 16:52:37 2005
@@ -23,10 +23,9 @@
   .local int counter
 
   .local pmc parse
-  .local pmc expression_p,expression_i
+  .local pmc expression_p
   parse = find_global "_Tcl", "parse"
   expression_p = find_global "_Tcl", "__expression_parse"
-  expression_i = find_global "_Tcl", "__expression_interpret"
  
   .local string temp_str
   temp_str ="" 
@@ -73,8 +72,8 @@ get_final:
   if counter != argc goto more_than_else
 
 begin_parsing:
-  retval = expression_p(condition)
-  retval = expression_i(retval)
+  $P1 = expression_p(condition)
+  retval = $P1()
 
   unless retval goto do_elseifs
   code = body 
@@ -88,8 +87,8 @@ elseif_loop:
   if $I2 == $I1 goto do_else
   $P1 = elseifs[$I2]
   condition = $P1[0]
-  retval = expression_p(condition)
-  retval = expression_i(retval)
+  $P2 = expression_p(condition)
+  retval = $P2()
   if retval goto done_elseifs
   inc $I2
   goto elseif_loop  

Modified: trunk/languages/tcl/lib/commands/while.pir
==============================================================================
--- trunk/languages/tcl/lib/commands/while.pir  (original)
+++ trunk/languages/tcl/lib/commands/while.pir  Wed Sep 28 16:52:37 2005
@@ -10,29 +10,23 @@
 
   if argc != 2 goto bad_args
 
-  .local pmc    cond_p
-  cond_p = argv[0]
-  .local string body_p
-  body_p = argv[1]
+  .local string condition,body
+  condition = argv[0]
+  body      = argv[1]
 
   .local pmc retval, parsed_code
 
   .local pmc parse
-  .local pmc expression_p
-  .local pmc expression_i
+  .local pmc expression_p, compiled_condition
 
   parse = find_global "_Tcl", "parse"
   expression_p = find_global "_Tcl", "__expression_parse"
-  expression_i = find_global "_Tcl", "__expression_interpret"
 
-  $S0 = body_p
-  parsed_code = parse($S0)
-  register parsed_code
+  parsed_code = parse(body)
+  compiled_condition = expression_p(condition)
 
 while_loop:
-  $S0 = cond_p
-  retval = expression_p($S0)
-  retval = expression_i(retval)
+  retval = compiled_condition()
   unless retval goto done
   push_eh handle_continue
     retval = parsed_code."interpret"()

Modified: trunk/languages/tcl/lib/expression.pir
==============================================================================
--- trunk/languages/tcl/lib/expression.pir      (original)
+++ trunk/languages/tcl/lib/expression.pir      Wed Sep 28 16:52:37 2005
@@ -1,25 +1,23 @@
-.macro __pop_value_from_expr_stack(STACK,VALUE)
-  .VALUE = pop .STACK
-  .VALUE = .VALUE[1]
-  .VALUE = .VALUE."interpret"()
-  .VALUE = __number(.VALUE)  # XXX unnecessary ?
-.endm
-
 .namespace [ "_Tcl" ]
 
+.const int MAX_PRECEDENCE =  11
+
 =head1 _Tcl::__expression_parse
 
-Given a string (or String), return a single stack that contains the work to be
-done. This stack can then be passed to C<__expression_interpret> to actually
-invoke the items on the stack.
+Given a string, return an invokable PMC that will generate the appropriate
+value described by this Tcl expression. An intermediate AST is generated,
+and the functions ends with a tailcall to the compiler.
 
 =cut
 
-.const int MAX_PRECEDENCE =  11
-
 .sub __expression_parse
   .param string expr
+  $P1 = __expression_ast(expr) 
+  .return __expression_compile($P1)
+.end
 
+.sub __expression_ast
+  .param string expr
   .local pmc retval
 
   .local pmc undef
@@ -40,11 +38,10 @@ operand:
   chunk[0] = OPERAND
   chunk[1] = retval
   push chunks, chunk
-
   goto operator
 
 no_operand:
-  .throw ("no operand!")
+  .throw ("XXX: no operand!")
 
 operator:
   (chunk, pos) = get_operator(expr, pos)
@@ -52,89 +49,103 @@ operator:
   push chunks, chunk
   goto operand
 
-  # if we don't match any of the possible cases so far, then we must
-  # be a string operand, but for now, die. #XXX
-
 chunks_done:
-# convert the chunks into a stack.
 
-  # to do this, we scan for our Operators in precedence order.
-  # as we find each one, put it on the program_stack with the appropriate
-  # args. Leave a "NOOP" placeholder when pulling things. If our target
-  # arg is a noop, we can either put it on the stack and ignore it when
-  # popping the stack, or not put it on the stack.
+=for comment
 
-  # XXX cheat for now , assume no precedence. means we can just
-  # walk through, grabbing ops. (hope nothing is orphaned?)
+Convert the chunks into a stack. For each level of precendence, 
+scan the chunk list for operators that match our level. As we find one, 
+grab the left and right operands. If the operand is null, instead use
+the value from the same location in the program stack as the operand (as it
+is the result of a previous operator.). If a value is pulled from the
+program_stack, then null it there.
+
+Now that we have an operand and two operators (unary ops are treated like
+operands during the parse phase. Ternary op is currently ignored, but we'd
+special case it here.), create a TclBinaryOp, and put it in the same
+index in the program stack as the left op here. Replace all three entries
+in the chunk list with a single NULL entry.
+
+When our list of chunks to process is a single NULL entry, we're done,
+and now have a program list containing a single element, which is something
+which supports C<compile>. (In most cases, a TclBinaryOp)
 
-  .local int stack_index
-  .local int input_len
+=cut
 
+  .local int stack_index
   stack_index = 0
 
- # we're looping over this once - to handle precedence, I suggest
- # looping multiple times, leaving the NOOPS when we remove something
- # to faciliate processing on further runs. If we try to pull a
- # left or right arg and see a NO-OP, we know it's safe to skip because
- # walking the stack will convert it to a number by the time we get to it.
-
-  .local pmc our_op
-  input_len  = chunks
-  if input_len == 0 goto die_horribly
-
-  # a single value, return now.
-  if input_len != 1 goto pre_converter_loop
-  # XXX (That's value, not an operator)
-  .return(chunks)
+  .local int input_len
+  input_len = chunks
+  if input_len == 1 goto singleton_chunk
 
-pre_converter_loop:
   .local int precedence_level
-  precedence_level = -1 # start with functions
+  precedence_level = 1
+  .local pmc operand1, operand2, our_op
+
 converter_loop:
+  input_len = chunks
+
   if precedence_level > MAX_PRECEDENCE goto converter_done
-  if stack_index >= input_len goto precedence_done
+  if input_len == 1 goto converter_done # only one item left. should be null
+  if stack_index >= input_len goto precedence_done 
+
   our_op = chunks[stack_index]
-  if_null our_op, converter_next
-  $I0 = typeof our_op
-  if $I0 == .Undef goto converter_next
+  unless our_op, converter_next  # skip placeholders
+  $I0 = defined our_op
+  unless $I0, converter_next      # skip placeholders (redundant?)
   $I2 = our_op[0]
   if $I2 == OPERAND goto converter_next
-  if $I2 == CHUNK   goto converter_next
-  if $I2 == OP   goto is_opfunc
-
-  # Should never be reached (XXX then shouldn't we die horribly?)
-  goto converter_next
 
-is_opfunc:
+# an_operator
   $I3 = our_op[2]
-  if $I3 != precedence_level goto converter_next
+  if $I3 != precedence_level goto converter_next 
+
+# right precedence level.
 
 right_arg:
   $I2 = stack_index + 1
-  if $I2 >= input_len goto left_arg
-  retval = chunks[$I2]
-  if_null retval, left_arg
-  chunks[$I2] = undef
-  inc $I4
-  program_stack = unshift retval
-
-  # If we're a function, (XXX) assume a single arg (which
-  # we've now pulled - so, go to the, skip the left arg.
-  if precedence_level == -1 goto shift_op
+  ### XXX Should never occur? if $I2 >= input_len goto left_arg  
+  $P1 = chunks[$I2]
+  unless $P1, right_arg_precalc
+  operand2 = $P1[1]
+  goto left_arg
+
+right_arg_precalc:
+  operand2 = program_stack[$I2]
+  program_stack[$I2] = $P1 
 
-  # XXX we just deal with binary args at the moment.
 left_arg:
   $I2 = stack_index - 1
-  if $I2 < 0 goto shift_op
-  retval = chunks[$I2]
-  if_null retval, shift_op
-  chunks[$I2] = undef
-  inc $I4
-  program_stack = unshift retval
+  ### XXX Should never occur? if $I2 < 0 goto shift_op
+  $P1 = chunks[$I2]
+  unless $P1, left_arg_precalc
+  operand1 = $P1[1]
+  goto shift_op
+
+left_arg_precalc:
+  operand1 = program_stack[$I2]
+  program_stack[$I2] = $P1 
 
 shift_op:
-  program_stack = unshift our_op
-  chunks[stack_index] = undef
+  .local pmc type
+  type = our_op[1]
+  $I0 = find_type "TclBinaryOp" # XXX should cache this?
+  .local pmc binary_op
+  binary_op = new $I0
+
+ 
+  setattribute binary_op, "TclBinaryOp\x00type", type
+  setattribute binary_op, "TclBinaryOp\x00l_operand", operand1
+  setattribute binary_op, "TclBinaryOp\x00r_operand", operand2
+
+  program_stack[$I2] = binary_op
+
+  delete chunks[$I2] # delete the left arg.
+  chunks[$I2] = 0    # zero the operator
+  inc $I2            # skip the operator position 
+  delete chunks[$I2] # delete the right arg.
+  dec stack_index    
 
 converter_next:
   inc stack_index
@@ -145,11 +156,18 @@ precedence_done:
   stack_index = 0
   goto converter_loop
 
+singleton_chunk:
+  # a single value, return now.
+  $P1 = chunks[0] # first element..
+  $P1 = $P1[1]    # value of first element.
+  .return ($P1)
+
 die_horribly:
-  .throw ("XXX: An error occurred in [expr]")
+  .throw ("XXX: An error occurred parsing [expr]")
 
 converter_done:
-  .return(program_stack)
+  $P1 = program_stack[0]
+  .return ($P1)
 
 .end
 
@@ -278,228 +296,6 @@ done:
   .return(chunk, pos)
 .end
 
-.sub __expression_interpret
-  .param pmc program_stack
-
-  .local pmc result_stack
-  result_stack = new TclList
-  .local pmc retval
-stack_evaluator:
- # while the prog stack exists:
- .local int size
- size = program_stack
- if size == 0 goto stack_done
-
- .local int type
- .local pmc chunk
- chunk = pop program_stack
- $I10 = typeof chunk
- if $I10 == .Undef goto stack_evaluator
- type = chunk[0]
-
- # move all non op non funcs to the value stack
- if type == OP goto do_op
- push result_stack, chunk
- goto stack_evaluator
-
-do_op:
-  # right now, we assume binary ops. Later, each op will define the
-  # number of and type of ops it takes, and we will respect it.
-
-  .local int op
-  op = chunk[1]
-
-  # XXX assume all operands take two args.
-  # XXX looks like there is code to convert everything to numbers.
-  #     - this will have to be changed for string ops.
-
-  .local pmc r_arg,l_arg,op_result
-  op_result = new TclInt
-
-  # Is there a more efficient way to do this dispatch?
-  if op == OPERATOR_MUL goto op_mul
-  if op == OPERATOR_DIV goto op_div
-  if op == OPERATOR_MOD goto op_mod
-  if op == OPERATOR_PLUS goto op_plus
-  if op == OPERATOR_MINUS goto op_minus
-  if op == OPERATOR_SHL goto op_shl
-  if op == OPERATOR_SHR goto op_shr
-  if op == OPERATOR_LT goto op_lt
-  if op == OPERATOR_GT goto op_gt
-  if op == OPERATOR_LTE goto op_lte
-  if op == OPERATOR_GTE goto op_gte
-  if op == OPERATOR_EQUAL goto op_equal
-  if op == OPERATOR_UNEQUAL goto op_unequal
-  if op == OPERATOR_BITAND goto op_bitand
-  if op == OPERATOR_BITXOR goto op_bitxor
-  if op == OPERATOR_BITOR goto op_bitor
-  if op == OPERATOR_NE goto op_ne
-  if op == OPERATOR_EQ goto op_eq
-  if op == OPERATOR_AND goto op_and
-  if op == OPERATOR_OR goto op_or
-
-  goto die_horribly # XXX should never happen, of course.
-
-op_mul:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = mul l_arg, r_arg
-  goto done_op
-op_div:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = div l_arg, r_arg
-  goto done_op
-op_mod:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = mod l_arg, r_arg
-  goto done_op
-op_plus:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = l_arg + r_arg
-  goto done_op
-op_minus:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = l_arg - r_arg
-  goto done_op
-op_shl:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = shl l_arg, r_arg
-  goto done_op
-op_shr:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = shr l_arg, r_arg
-  goto done_op
-op_lt:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = 1
-  if l_arg < r_arg goto done_op
-  op_result = 0
-  goto done_op
-op_gt:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = 1
-  if l_arg > r_arg goto done_op
-  op_result = 0
-  goto done_op
-op_lte:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = 1
-  if l_arg <= r_arg goto done_op
-  op_result = 0
-  goto done_op
-op_gte:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = 1
-  if l_arg >= r_arg goto done_op
-  op_result = 0
-  goto done_op
-op_equal:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = 1
-  if l_arg == r_arg goto done_op
-  op_result = 0
-  goto done_op
-op_unequal:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = 1
-  if l_arg != r_arg goto done_op
-  op_result = 0
-  goto done_op
-op_bitand:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = band l_arg, r_arg
-  goto done_op
-op_bitxor:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = bxor l_arg, r_arg
-  goto done_op
-op_bitor:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = bor l_arg, r_arg
-  goto done_op
-op_ne:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = 1
-  $S0 = l_arg
-  $S1 = r_arg
-  if $S0 != $S1 goto done_op
-  op_result = 0
-  goto done_op
-op_eq:
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  op_result = 1
-  $S0 = l_arg
-  $S1 = r_arg
-  if $S0 == $S1 goto done_op
-  op_result = 0
-  goto done_op
-op_and:
-  op_result = 0
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  unless l_arg goto done_op
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  unless r_arg goto done_op
-  op_result = 1
-  goto done_op
-op_or:
-  op_result = 1
-  .__pop_value_from_expr_stack(result_stack,l_arg)
-  if l_arg goto done_op
-  .__pop_value_from_expr_stack(result_stack,r_arg)
-  if r_arg goto done_op
-  op_result = 0
-  # goto done_op
-
-done_op:
-  $P5 = new TclList
-  $P5[0] = OPERAND
-  $P5[1] = op_result
-  push result_stack, $P5
-
-  # Ignoring exceptions for now.
-  goto stack_evaluator
-
-stack_done:
-  $I0 = result_stack
-  if $I0 == 0 goto die_horribly
-  retval = pop result_stack
-  goto evaluation_done
-
-die_horribly:
-  .throw ("XXX: an error occurred in [expr]")
-
-evaluation_done:
-  retval = retval[1]
-
-  # XXX This is a bit of a hack. We should insure that everything we get at 
this
-  # point is either interpret-able or not.
-
-  $I0 = can retval, "interpret"
-  if $I0 goto done_interp
-  .return (retval)
-
-done_interp:
-  .return retval."interpret"()
-
-.end
-
 .sub get_subexpr
   .param string expr
   .param int pos
@@ -534,14 +330,11 @@ paren_done:
   inc pos
   $S1 = substr expr, start, $I0
 
-  # XXX this is now officially braindead. Fissit.
-  retval = __expression_parse($S1)
-  retval = __expression_interpret(retval)
-
+  retval = __expression_ast($S1)
   .return(retval, pos)
 
 die_horribly:
-  .throw("XXX: An error occurred in EXPR")
+  .throw("XXX: An error occurred processing a sub-expression")
 
 premature_end:
   $S0 = "syntax error in expression \""
@@ -666,7 +459,7 @@ loop_done:
   $I0 = pos - paren_pos
   $S1 = substr expr, paren_pos, $I0
 
-  operand = __expression_parse($S1)
+  operand = __expression_ast($S1)
   setattribute func, "TclFunc\x00argument", operand
 
 done:
@@ -711,3 +504,42 @@ unknown_func:
   .return(unary, pos)
 .end
 
+=head1 _Tcl::__expression_compile
+
+Given the AST generated by the expression parser, render the various operands
+as PIR. 
+
+=cut
+
+.sub __expression_compile
+  .param pmc thing
+ 
+   .local pmc compile
+   compile = find_global "_Tcl", "compile"
+   .local string pir_code
+
+   ($I0,pir_code) = compile(thing,0)
+
+  .local pmc pir_compiler
+  pir_compiler = compreg "PIR"
+  # XXX deal with re-using sub name?
+
+  $P1 = new .Array
+  $P1 = 2
+  $P1[0] = pir_code
+  $P1[1] = $I0
+
+  # Use n_operators pragma to force generation of new pmcs 
+  sprintf pir_code, ".pragma n_operators 1\n.sub blah @ANON\n%s.return 
($P%s)\n.end\n", $P1
+
+  #print pir_code # for debugging the compiler
+
+  # XXX HACK: can't tailcall these.  
+  $P1 = pir_compiler(pir_code)
+  .return ($P1)
+
+die_horribly:
+  .throw ("XXX: an error occurred compiling [expr]")
+
+.end
+

Modified: trunk/languages/tcl/lib/parser.pir
==============================================================================
--- trunk/languages/tcl/lib/parser.pir  (original)
+++ trunk/languages/tcl/lib/parser.pir  Wed Sep 28 16:52:37 2005
@@ -163,7 +163,7 @@ get:
   $I0 = find_type "TclCommand"
   command = new $I0
   setattribute command, "TclCommand\x00name", word
-  
+
 next_word:
   (word, pos) = get_word(tcl_code, chars, pos)
   if_null word, done
@@ -493,13 +493,13 @@ Parses a subcommand and returns a TclCom
   .param string tcl_code
   .param int pos
   inc pos
-  
+
   .local pmc command
   .local pmc chars
   chars = new Hash
   chars[93] = 1 # ]
   chars[59] = 1 # ;
-  
+ 
   (command, pos) = get_command(tcl_code, chars, pos)
   dec pos
   $I0 = ord tcl_code, pos
@@ -609,6 +609,53 @@ done:
   .return($P0, pos)
 .end
 
+
+=item C<(int reg_num, str code) = compile(pmc thing, int reg_num)>
+
+Given an object, call its compile method, or assume it's constant
+and generate the code for it.
+
+=cut
+
+.sub compile
+  .param pmc thing
+  .param int register_num
+
+  .local string pir_code
+
+  $I0 = can thing, "compile"
+
+  if $I0 goto can_compile
+
+  .local string thing_type,quote_char
+  thing_type = typeof thing
+  quote_char = "" 
+  if thing_type == "TclString" goto stringish
+  if thing_type == "String" goto    stringish
+  goto set_args
+stringish:
+  quote_char = "\"" 
+set_args:
+
+  .local pmc printf_args
+  printf_args = new .Array
+  printf_args = 6
+  printf_args[0] = register_num
+  printf_args[1] = thing_type
+  printf_args[2] = register_num
+  printf_args[3] = quote_char
+  printf_args[4] = thing
+  printf_args[5] = quote_char
+ 
+  pir_code = sprintf "$P%i = new .%s\n$P%i=%s%s%s\n", printf_args
+
+  .return(register_num,pir_code)
+
+can_compile:
+  .return thing."compile"(register_num)
+.end
+
 =back
 
 =cut
+

Modified: trunk/languages/tcl/lib/tclcommand.pir
==============================================================================
--- trunk/languages/tcl/lib/tclcommand.pir      (original)
+++ trunk/languages/tcl/lib/tclcommand.pir      Wed Sep 28 16:52:37 2005
@@ -23,10 +23,19 @@ Initialize the attributes for an instanc
 =cut
 
 .sub __init method
-  $P0 = new TclString
-  setattribute self, "TclCommand\x00name", $P0
+  #$P0 = new TclString
+  #setattribute self, "TclCommand\x00name", $P0
 .end
 
+.sub __clone method
+  .local pmc obj
+  $I0 = typeof self
+  obj = new $I0
+  obj = self
+  .return(obj)
+.end
+
+
 =head2 interpret
 
 Execute the command.
@@ -90,3 +99,85 @@ no_command_non_interactive:
   $S0 .= "\""
   .throw($S0)
 .end
+
+.sub compile method
+   .param int register_num
+
+   $P22 = getattribute self, "TclCommand\x00name"
+
+   .local pmc compile
+   compile = find_global "_Tcl", "compile"
+
+   .local string pir_code
+   pir_code = ""
+
+   .local string retval
+   # Generate partial code for each of our arguments
+   .local int ii, num_args, result_reg
+   num_args = self 
+   ii = 0
+   .local pmc compiled_args
+   compiled_args = new .TclList
+arg_loop:
+   if ii == num_args goto arg_loop_done
+   $P1 = self[ii]
+   (result_reg,retval) = compile($P1,register_num)
+
+   push compiled_args, result_reg
+   register_num = result_reg + 1
+   pir_code .= retval 
+   inc ii 
+arg_loop_done:
+   # Generate code that will invoke our name'd command.
+   pir_code .= ".local pmc command,name\n"
+   # XXX Need to trap a missing command
+   # Need to actually compile our name, as it might not be constant.
+
+   .local pmc name
+   name = getattribute self, "TclCommand\x00name"
+   (register_num,retval) = compile(name,register_num)
+
+   pir_code .= retval
+   $S1 = "$P"
+   $S0 = register_num
+   $S1 .= $S0
+
+   inc register_num
+   $S0 = register_num
+   $S2 = "$S"
+   $S2 .= $S0
+
+   # Get a string version of the name
+   pir_code .= $S2
+   pir_code .= "="
+   pir_code .= $S1
+   pir_code .= "\n"
+   
+   # Prepend a "&"
+   pir_code .= $S2
+   pir_code .= " = \"&\" . "
+   pir_code .= $S2
+   pir_code .= "\n"
+  
+   pir_code .= "\ncommand = find_global \"Tcl\", "
+   pir_code .= $S2
+   pir_code .= "\n$P"
+
+   pir_code .= $S0
+   pir_code .= " = command("
+   ii = 0
+elem_loop:
+   if ii == num_args goto elem_loop_done   
+   $S0 = compiled_args[ii]
+   pir_code .= "$P"
+   pir_code .= $S0
+   inc ii 
+   if ii == num_args goto elem_loop_done
+   pir_code .= ","
+   goto elem_loop 
+elem_loop_done:
+   pir_code .= ")\n"
+
+   # return the code and the new register_num 
+  .return (register_num,pir_code)
+.end

Modified: trunk/languages/tcl/lib/tclconst.pir
==============================================================================
--- trunk/languages/tcl/lib/tclconst.pir        (original)
+++ trunk/languages/tcl/lib/tclconst.pir        Wed Sep 28 16:52:37 2005
@@ -266,3 +266,24 @@ Get the value of the const.
 .sub interpret method
     .return(self)
 .end
+
+=head2 compile
+
+Generate PIR code which can be used to generate our value
+
+=cut
+
+.sub compile method
+   .param int argnum
+   
+   .local string code
+
+   .local pmc value
+   $I0 = classoffset self, "TclConst"
+   value = getattribute self, $I0
+
+   .local pmc compiler
+   compiler = find_global "_Tcl", "compile"
+ 
+   .return compiler(value,argnum)
+.end

Modified: trunk/languages/tcl/lib/tclfunc.pir
==============================================================================
--- trunk/languages/tcl/lib/tclfunc.pir (original)
+++ trunk/languages/tcl/lib/tclfunc.pir Wed Sep 28 16:52:37 2005
@@ -47,25 +47,31 @@ Initialize the attributes for an instanc
   setattribute self, "TclFunc\x00argument", $P0
 .end
 
-.sub interpret method
-  .local pmc retval
-  # assigning a $Nx will change this to a TclFloat
-
-  retval      = new TclInt
+.sub compile method
+  .param int register_num
 
-  .local pmc funcs, expr_interpret, __number
+  .local pmc retval
+  .local string pir_code
+  .local pmc funcs,  __number, compile
   funcs = find_global "_Tcl", "functions"
-  expr_interpret = find_global "_Tcl", "__expression_interpret"
   __number = find_global "_Tcl", "__number"
-  
-  .local pmc arg, name
-  arg  = getattribute self, "TclFunc\x00argument"
-  retval = expr_interpret(arg)
+  compile = find_global  "_Tcl", "compile" 
+
+  # eventually, we'll need to deal with more than one arg.
+
+  .local pmc arg_code, arg_reg, name
+  $P1  = getattribute self, "TclFunc\x00argument"
+  (arg_reg,pir_code)  = compile($P1,register_num)
+  inc register_num
+
+  # XXX We shouldn't store the name. we should store the opcode, avoid
+  # the lookup cost. (at least for builtins)
 
-  arg  = __number(retval)
   name = getattribute self, "TclFunc\x00name"
-  
   $I0 = funcs[name]
+
+  .local string opcode_name
+ 
   if $I0 == FUNCTION_ABS goto func_abs
   if $I0 == FUNCTION_ACOS goto func_acos
   if $I0 == FUNCTION_ASIN goto func_asin
@@ -82,75 +88,98 @@ Initialize the attributes for an instanc
   if $I0 == FUNCTION_TANH goto func_tanh
   
 func_abs:
-  retval = abs arg
-  goto done
+  .local pmc printf_args
+  printf_args = new .Array
+  printf_args = 2
+  printf_args[0] = register_num
+  printf_args[1] = arg_reg
+
+  pir_code .= "$P%i = abs $P%i\n"
+  goto done_all
+
 func_acos:
-  $N0 = arg
-  $N1 = acos $N0
-  retval = $N1
+  opcode_name = "acos"
   goto done
+
 func_asin:
-  $N0 = arg
-  $N1 = asin $N0
-  retval = $N1
+  opcode_name = "asin"
   goto done
+
 func_atan:
-  $N0 = arg
-  $N1 = atan $N0
-  retval = $N1
+  opcode_name = "atan"
   goto done
+
 func_cos:
-  $N0 = arg
-  $N1 = cos $N0
-  retval = $N1
+  opcode_name = "cos"
   goto done
+
 func_cosh:
-  $N0 = arg
-  $N1 = cosh $N0
-  retval = $N1
+  opcode_name = "cosh"
   goto done
+
 func_exp:
-  $N0 = arg
-  $N1 = exp $N0
-  retval = $N1
+  opcode_name = "exp"
   goto done
+
 func_log:
-  $N0 = arg
-  $N1 = ln $N0
-  retval = $N1
+  opcode_name = "ln"
   goto done
+
 func_log10:
-  $N0 = arg
-  $N1 = log10 $N0
-  retval = $N1
+  opcode_name = "log10"
   goto done
+
 func_sin:
-  $N0 = arg
-  $N1 = sin $N0
-  retval = $N1
+  opcode_name = "sin"
   goto done
+
 func_sinh:
-  $N0 = arg
-  $N1 = sinh $N0
-  retval = $N1
+  opcode_name = "sinh"
   goto done
+
 func_sqrt:
-  $N0 = arg
-  $N1 = sqrt $N0
-  retval = $N1
+  opcode_name = "sqrt"
   goto done
+
 func_tan:
-  $N0 = arg
-  $N1 = tan $N0
-  retval = $N1
+  opcode_name = "tan"
   goto done
+
 func_tanh:
-  $N0 = arg
-  $N1 = tanh $N0
-  retval = $N1
-  #goto done
+  opcode_name = "tanh"
+
 
 done:
-  .return(retval)
+  .local pmc printf_args
+  printf_args = new .Array
+  printf_args = 8
+  printf_args[0] = register_num
+  printf_args[1] = arg_reg
+  printf_args[2] = register_num
+  printf_args[3] = opcode_name
+  printf_args[4] = register_num
+  printf_args[5] = register_num
+  printf_args[6] = register_num
+  printf_args[7] = register_num
+
+  pir_code .= "$N%i=$P%i\n"
+  pir_code .= "$N%i=%s $N%i\n"
+  pir_code .= "$P%i = new .TclFloat\n"
+  pir_code .= "$P%i=$N%i\n"
+
+
+done_all:
+
+  pir_code = sprintf pir_code, printf_args
+  .return(register_num,pir_code)
 
 .end
+
+.sub __clone method
+  .local pmc obj
+  $I0 = typeof self
+  obj = new $I0
+  obj = self
+  .return(obj)
+.end
+

Modified: trunk/languages/tcl/lib/tclops.pir
==============================================================================
--- trunk/languages/tcl/lib/tclops.pir  (original)
+++ trunk/languages/tcl/lib/tclops.pir  Wed Sep 28 16:52:37 2005
@@ -62,3 +62,56 @@ done:
   .return(retval)
 
 .end
+
+.sub compile method
+  .param int register_num
+
+  .local string pir_code
+
+  .local pmc retval
+  retval = new TclInt
+  
+  .local pmc name, operand, compile
+  name    = getattribute self, "TclUnaryOp\x00name"
+  operand = getattribute self, "TclUnaryOp\x00operand"
+  compile = find_global "_Tcl", "compile"
+
+  .local string opcode
+ 
+  if name == "-" goto minus
+  if name == "+" goto plus
+  if name == "~" goto bitwise_not
+  if name == "!" goto logical_not
+  
+minus:
+  opcode = "neg" 
+  goto done
+
+plus: 
+  .return compile(operand,register_num) 
+
+bitwise_not:
+  opcode = "bnot"
+  goto done
+
+logical_not:
+  opcode = "not" 
+done:
+  (register_num,pir_code) = compile(operand,register_num)
+
+  .local pmc printf_args
+  printf_args = new .Array
+  printf_args = 3
+  inc register_num
+  printf_args[0] = register_num
+  printf_args[1] = opcode
+  dec register_num
+  printf_args[2] = register_num
+  inc register_num
+
+  $S0 = sprintf "$P%i=%s $P%i\n", printf_args
+  pir_code .= $S0
+
+  .return(register_num, pir_code)
+
+.end

Modified: trunk/languages/tcl/lib/tclvar.pir
==============================================================================
--- trunk/languages/tcl/lib/tclvar.pir  (original)
+++ trunk/languages/tcl/lib/tclvar.pir  Wed Sep 28 16:52:37 2005
@@ -27,3 +27,22 @@ Get the value of the variable.
     $S0 = self
     .return read($S0)
 .end
+
+.sub compile method
+    .param int register_num
+
+    .local string pir_code
+    .local pmc args
+ 
+    args = new .Array
+    args = 4
+    args[0] = register_num
+    $S0 = self
+    args[1] = $S0
+    args[2] = register_num
+    args[3] = register_num
+
+    pir_code = sprintf ".local pmc read\nread=find_global \"_Tcl\", 
\"__read\"\n.local pmc number\nnumber=find_global \"_Tcl\", \"__number\"\n$P%i 
= read(\"%s\")\n$P%i = number($P%i)\n", args
+  
+    .return (register_num,pir_code)
+.end

Modified: trunk/languages/tcl/t/cmd_expr.t
==============================================================================
--- trunk/languages/tcl/t/cmd_expr.t    (original)
+++ trunk/languages/tcl/t/cmd_expr.t    Wed Sep 28 16:52:37 2005
@@ -2,9 +2,15 @@
 
 use strict;
 use lib qw(tcl/t t . ../lib ../../lib ../../../lib);
-use Parrot::Test tests => 49;
+use Parrot::Test tests => 52;
 use Test::More;
 
+language_output_is("tcl",<<TCL,<<OUT,"int");
+ puts [expr 42]
+TCL
+42
+OUT
+
 language_output_is("tcl",<<TCL,<<OUT,"mul");
  puts [expr 2 * 3]
 TCL
@@ -48,6 +54,19 @@ TCL
 4
 OUT
 
+language_output_is("tcl",<<TCL,<<OUT,"lt, numeric, not alpha...");
+ puts [expr 10 < 9]
+TCL
+0
+OUT
+
+language_output_is("tcl",<<'TCL',<<OUT,"lt, numeric, not alpha, with vars");
+ set a 10
+ puts [expr $a < 9]
+TCL
+0
+OUT
+
 language_output_is("tcl",<<TCL,<<OUT,"lt, true");
  puts [expr 2 < 3]
 TCL
@@ -160,7 +179,7 @@ F
 0
 OUT
 
-language_output_is("tcl",<<TCL,<<OUT,"&&, both sides");
+language_output_is("tcl",<<TCL,<<OUT,"||, both sides");
  proc true {} {puts T; return 1}
  proc false {} {puts F; return 0}
  puts [expr {[false] || [true]}]

Modified: trunk/languages/tcl/t/tcl_misc.t
==============================================================================
--- trunk/languages/tcl/t/tcl_misc.t    (original)
+++ trunk/languages/tcl/t/tcl_misc.t    Wed Sep 28 16:52:37 2005
@@ -147,7 +147,3 @@ language_output_is("tcl",<<'TCL',<<'OUT'
 TCL
 ok
 OUT
-
-
-
-

Modified: trunk/languages/tcl/tcl.pir_template
==============================================================================
--- trunk/languages/tcl/tcl.pir_template        (original)
+++ trunk/languages/tcl/tcl.pir_template        Wed Sep 28 16:52:37 2005
@@ -23,7 +23,6 @@ providing a compreg-compatible method.
  
   # Still not sure if these are going to be useful
   .const int BLOCK   = 14
-  .const int CHUNK   = 15
   .const int COMMAND = 16
 
 
@@ -49,6 +48,7 @@ providing a compreg-compatible method.
   .const int OPERATOR_SHL    = 43
   .const int OPERATOR_SHR    = 44
   .const int OPERATOR_UNEQUAL= 45
+
   .const int FUNCTION_ATAN2  = 46
   .const int FUNCTION_FMOD   = 47
   .const int FUNCTION_POW    = 48
@@ -120,7 +120,7 @@ providing a compreg-compatible method.
   operators = new TclArray
   precedence = new TclArray
 
-  # This precedence check should be shoved into [expr]. There's no need
+  # XXX This precedence check should be shoved into [expr]. There's no need
   # to make it this generic. 
 
   operators["*"] = OPERATOR_MUL

Modified: trunk/languages/tcl/tcl.pl
==============================================================================
--- trunk/languages/tcl/tcl.pl  (original)
+++ trunk/languages/tcl/tcl.pl  Wed Sep 28 16:52:37 2005
@@ -50,6 +50,8 @@ Generate the PIR code that matches the v
 my $rulefile = "lib/tcl.p6r";
 my $rules;
 
+=for later
+
 open (RULES,$rulefile) or die "can't read rules file.\n";
 
 $rules = <<'EOH';
@@ -81,6 +83,10 @@ EORULE
 
 $rules .= ".end\n";
 
+=cut
+
+$rules = q{};
+
 $contents =~ s/\${INCLUDES}/$includes/g;
 $contents =~ s/\${HEADER}/This file automatically generated, do not edit./g;
 $contents =~ s/\${RULES}/$rules/g;

Reply via email to