Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1379 lines (1150 sloc) 37.2 KB
require_relative '../test_helper'
class TestInterpreter < Test::Unit::TestCase
include TPPlus::Nodes
def setup
@scanner = TPPlus::Scanner.new
@parser = TPPlus::Parser.new @scanner
@interpreter = @parser.interpreter
end
def parse(s)
@scanner.scan_setup(s)
@parser.parse
end
def last_node
@last_node ||= @interpreter.nodes.last
end
def assert_node_type(t, n)
assert_equal t, n.class
end
def assert_prog(s)
assert_equal s, @interpreter.eval
end
def test_blank_prog
parse("")
assert_prog ""
end
def test_definition
parse("foo := R[1]")
assert_prog ""
end
def test_multi_define_fails
parse("foo := R[1]\nfoo := R[2]")
assert_raise(RuntimeError) do
assert_prog ""
end
end
def test_var_usage
parse("foo := R[1]\nfoo = 1")
assert_prog "R[1:foo]=1 ;\n"
end
def test_basic_addition
parse("foo := R[1]\nfoo = 1 + 1")
assert_prog "R[1:foo]=1+1 ;\n"
end
def test_basic_addition_with_var
parse("foo := R[1]\n foo = foo + 1")
assert_prog "R[1:foo]=R[1:foo]+1 ;\n"
end
def test_label_definition
parse("@foo")
assert_prog "LBL[100:foo] ;\n"
end
def test_duplicate_label_definition
parse("@foo\n@foo")
assert_raise RuntimeError do
assert_prog ""
end
end
def test_jump_to_label
parse("@foo\njump_to @foo")
assert_prog "LBL[100:foo] ;\nJMP LBL[100] ;\n"
end
def test_nonexistent_label_error
parse("jump_to @foo")
assert_raise RuntimeError do
assert_prog ""
end
end
def test_turn_on
parse("foo := DO[1]\nturn_on foo")
assert_prog "DO[1:foo]=ON ;\n"
end
def test_turn_off
parse("foo := DO[1]\nturn_off foo")
assert_prog "DO[1:foo]=OFF ;\n"
end
def test_toggle
parse("foo := DO[1]\ntoggle foo")
assert_prog "DO[1:foo]=(!DO[1:foo]) ;\n"
end
def test_simple_linear_motion
parse("foo := PR[1]\nlinear_move.to(foo).at(2000, 'mm/s').term(0)")
assert_prog "L PR[1:foo] 2000mm/sec CNT0 ;\n"
end
def test_simple_if
parse("foo := R[1]\nif foo==1\nfoo=2\nend")
assert_prog "IF (R[1:foo]=1),R[1:foo]=(2) ;\n"
end
def test_simple_if_else
parse("foo := R[1]\nif foo==1\nfoo=2\nelse\nfoo=1\nend")
assert_prog "IF R[1:foo]<>1,JMP LBL[100] ;\nR[1:foo]=2 ;\nJMP LBL[101] ;\nLBL[100] ;\nR[1:foo]=1 ;\nLBL[101] ;\n"
end
def test_simple_unless
parse("foo := R[1]\nunless foo==1\nfoo=2\nend")
assert_prog "IF (R[1:foo]<>1),R[1:foo]=(2) ;\n"
end
def test_simple_unless_else
parse("foo := R[1]\nunless foo==1\nfoo=2\nelse\nfoo=1\nend")
assert_prog "IF R[1:foo]=1,JMP LBL[100] ;\nR[1:foo]=2 ;\nJMP LBL[101] ;\nLBL[100] ;\nR[1:foo]=1 ;\nLBL[101] ;\n"
end
def test_comment
parse("# this is a comment")
assert_prog "! this is a comment ;\n"
end
def test_two_comments
parse("# comment one\n# comment two")
assert_prog "! comment one ;\n! comment two ;\n"
end
def test_inline_comment
parse("foo := R[1] # comment\nfoo = 1 # another comment")
assert_prog "! comment ;\nR[1:foo]=1 ;\n! another comment ;\n"
end
def test_inline_conditional_if_on_jump
parse("foo := R[1]\n@bar\njump_to @bar if foo==1\n")
assert_prog "LBL[100:bar] ;\nIF R[1:foo]=1,JMP LBL[100] ;\n"
end
def test_inline_conditional_unless_on_jump
parse("foo := R[1]\n@bar\njump_to @bar unless foo==1\n")
assert_prog "LBL[100:bar] ;\nIF R[1:foo]<>1,JMP LBL[100] ;\n"
end
def test_inline_assignment
parse("foo := R[1]\nfoo=2 if foo==1\n")
assert_prog "IF (R[1:foo]=1),R[1:foo]=(2) ;\n"
end
def test_inline_io_method
parse("foo := DO[1]\nbar := R[1]\nturn_on foo if bar < 10\n")
assert_prog "IF (R[1:bar]<10),DO[1:foo]=(ON) ;\n"
end
def test_program_call
parse("foo()")
assert_prog "CALL FOO ;\n"
end
def test_program_call_with_simple_arg
parse("foo(1)")
assert_prog "CALL FOO(1) ;\n"
end
def test_program_call_with_multiple_simple_args
parse("foo(1,2,3)")
assert_prog "CALL FOO(1,2,3) ;\n"
end
def test_program_call_with_variable_argument
parse("foo := R[1]\nbar(foo)")
assert_prog "CALL BAR(R[1:foo]) ;\n"
end
def test_preserve_whitespace
parse("\n\n")
assert_prog " ;\n"
end
def test_plus_equals
parse("foo := R[1]\nfoo += 1\n")
assert_prog "R[1:foo]=R[1:foo]+1 ;\n"
end
def test_minus_equals
parse("foo := R[1]\nfoo -= 1\n")
assert_prog "R[1:foo]=R[1:foo]-1 ;\n"
end
def test_motion_to_a_position
parse("foo := P[1]\nlinear_move.to(foo).at(2000, 'mm/s').term(0)")
assert_prog "L P[1:foo] 2000mm/sec CNT0 ;\n"
end
def test_joint_move
parse("foo := P[1]\njoint_move.to(foo).at(100, '%').term(0)")
assert_prog "J P[1:foo] 100% CNT0 ;\n"
end
def test_joint_move_throws_error_with_bad_units
parse("foo := P[1]\njoint_move.to(foo).at(2000, 'mm/s').term(0)")
assert_raise(RuntimeError) do
assert_prog "J P[1:foo] 100% CNT0 ;\n"
end
end
def test_linear_move_throws_error_with_bad_units
parse("foo := P[1]\nlinear_move.to(foo).at(100, '%').term(0)")
assert_raise(RuntimeError) do
assert_prog "L P[1:foo] 100% CNT0 ;\n"
end
end
def test_pr_offset
parse("home := P[1]\nmy_offset := PR[1]\nlinear_move.to(home).at(2000, 'mm/s').term(0).offset(my_offset)")
assert_prog "L P[1:home] 2000mm/sec CNT0 Offset,PR[1:my_offset] ;\n"
end
def test_vr_offset
parse("home := P[1]\nvoff := VR[1]\nlinear_move.to(home).at(2000, 'mm/s').term(0).vision_offset(voff)")
assert_prog "L P[1:home] 2000mm/sec CNT0 VOFFSET,VR[1:voff] ;\n"
end
def test_time_before
parse("p := P[1]\nlinear_move.to(p).at(2000, 'mm/s').term(0).time_before(0.5, foo())")
assert_prog "L P[1:p] 2000mm/sec CNT0 TB .50sec,CALL FOO ;\n"
end
def test_time_after
parse("p := P[1]\nlinear_move.to(p).at(2000, 'mm/s').term(0).time_after(0.5, foo())")
assert_prog "L P[1:p] 2000mm/sec CNT0 TA .50sec,CALL FOO ;\n"
end
def test_time_before_with_register_time
parse("p := P[1]\nt := R[1]\nlinear_move.to(p).at(2000, 'mm/s').term(0).time_before(t, foo())")
assert_prog "L P[1:p] 2000mm/sec CNT0 TB R[1:t]sec,CALL FOO ;\n"
end
def test_time_before_with_io_method
parse("p := P[1]\nbar := DO[1]\nlinear_move.to(p).at(2000, 'mm/s').term(0).time_before(0.5, turn_on bar)")
assert_prog "L P[1:p] 2000mm/sec CNT0 TB .50sec,DO[1:bar]=ON ;\n"
end
def test_motion_with_indirect_termination
parse("p := P[1]\ncnt := R[1]\nlinear_move.to(p).at(2000, 'mm/s').term(cnt)")
assert_prog "L P[1:p] 2000mm/sec CNT R[1:cnt] ;\n"
end
def test_motion_with_indirect_speed
parse("p := P[1]\nspeed := R[1]\nlinear_move.to(p).at(speed, 'mm/s').term(0)")
assert_prog "L P[1:p] R[1:speed]mm/sec CNT0 ;\n"
end
def test_motion_with_max_speed
parse("p := P[1]\nlinear_move.to(p).at('max_speed').term(0)")
assert_prog "L P[1:p] max_speed CNT0 ;\n"
end
def test_use_uframe
parse("use_uframe 5")
assert_prog "UFRAME_NUM=5 ;\n"
end
def test_indirect_uframe
parse("foo := R[1]\nuse_uframe foo")
assert_prog "UFRAME_NUM=R[1:foo] ;\n"
end
def test_use_utool
parse("use_utool 5")
assert_prog "UTOOL_NUM=5 ;\n"
end
def test_indirect_utool
parse("foo := R[1]\nuse_utool foo")
assert_prog "UTOOL_NUM=R[1:foo] ;\n"
end
def test_payload
parse("use_payload 1")
assert_prog "PAYLOAD[1] ;\n"
end
def test_indirect_payload
parse("foo := R[1]\nuse_payload foo")
assert_prog "PAYLOAD[R[1:foo]] ;\n"
end
def test_nested_conditionals
parse("foo := R[1]\nif foo==1\nif foo==2\nfoo=3\nelse\nfoo=4\nend\nend")
assert_prog "IF R[1:foo]<>1,JMP LBL[100] ;\nIF R[1:foo]<>2,JMP LBL[101] ;\nR[1:foo]=3 ;\nJMP LBL[102] ;\nLBL[101] ;\nR[1:foo]=4 ;\nLBL[102] ;\nLBL[100] ;\n"
end
def test_inline_unless
parse("foo := R[1]\n@bar\njump_to @bar unless foo > 1")
assert_prog "LBL[100:bar] ;\nIF R[1:foo]<=1,JMP LBL[100] ;\n"
end
def test_inline_unless_with_two_vars
parse("foo := R[1]\nbar := R[2]\n@baz\njump_to @baz unless foo > bar")
assert_prog "LBL[100:baz] ;\nIF R[1:foo]<=R[2:bar],JMP LBL[100] ;\n"
end
def test_labels_can_be_defined_after_jumps_to_them
parse("jump_to @foo\n@foo")
assert_prog "JMP LBL[100] ;\nLBL[100:foo] ;\n"
end
def test_multiple_motion_modifiers
parse("p := P[1]\no := PR[1]\nlinear_move.to(p).at('max_speed').term(0).offset(o).time_before(0.5,foo())")
assert_prog "L P[1:p] max_speed CNT0 Offset,PR[1:o] TB .50sec,CALL FOO ;\n"
end
def test_motion_modifiers_swallow_terminators_after_dots
parse("p := P[1]\no := PR[1]\nlinear_move.\nto(p).\nat('max_speed').\nterm(0).\noffset(o).\ntime_before(0.5,foo())")
assert_prog "L P[1:p] max_speed CNT0 Offset,PR[1:o] TB .50sec,CALL FOO ;\n"
end
def test_wait_for_with_seconds
parse("wait_for(5,'s')")
assert_prog "WAIT 5.00(sec) ;\n"
end
def test_wait_for_with_invalid_units_throws_error
parse("wait_for(5,'ns')")
assert_raise(RuntimeError) do
assert_prog ""
end
end
def test_wait_for_with_milliseconds
parse("wait_for(100,'ms')")
assert_prog "WAIT .10(sec) ;\n"
end
def test_wait_for_with_indirect_seconds
parse "foo := R[1]\nwait_for(foo, 's')"
assert_prog "WAIT R[1:foo] ;\n"
end
def test_wait_for_with_indirect_ms
parse "foo := R[1]\nwait_for(foo, 'ms')"
assert_raise_message("Runtime error on line 2:\nIndirect values can only use seconds ('s') as the units argument") do
assert_prog ""
end
end
def test_wait_until_with_exp
parse("wait_until(1==0)")
assert_prog "WAIT (1=0) ;\n"
end
def test_wait_until_with_flag
parse("foo := F[1]\nwait_until(foo)")
assert_prog "WAIT (F[1:foo]) ;\n"
end
def test_wait_until_with_di
parse("foo := DI[1]\nwait_until(foo)")
assert_prog "WAIT (DI[1:foo]) ;\n"
end
def test_wait_until_with_boolean
parse("foo := DI[1]\nbar := DI[2]\nwait_until(foo && bar)")
assert_prog "WAIT (DI[1:foo] AND DI[2:bar]) ;\n"
end
def test_wait_until_with_timeout_to
parse("wait_until(1==0).timeout_to(@end)\n@end")
assert_prog "WAIT (1=0) TIMEOUT,LBL[100] ;\nLBL[100:end] ;\n"
end
def test_wait_until_with_timeout_to_and_after
parse("wait_until(1==0).timeout_to(@end).after(1, 's')\n@end")
assert_prog "$WAITTMOUT=(100) ;\nWAIT (1=0) TIMEOUT,LBL[100] ;\nLBL[100:end] ;\n"
end
def test_wait_until_after_ms
parse("wait_until(1==0).timeout_to(@end).after(1000, 'ms')\n@end")
assert_prog "$WAITTMOUT=(100) ;\nWAIT (1=0) TIMEOUT,LBL[100] ;\nLBL[100:end] ;\n"
end
def test_wait_until_after_indirect
parse("foo := R[1]\nwait_until(1==0).timeout_to(@end).after(foo, 's')\n@end")
assert_prog "$WAITTMOUT=(R[1:foo]*100) ;\nWAIT (1=0) TIMEOUT,LBL[100] ;\nLBL[100:end] ;\n"
end
def test_wait_until_with_constant
parse("WAIT := 5\nwait_until(1==0).timeout_to(@end).after(WAIT, 's')\n@end")
assert_prog "$WAITTMOUT=(5*100) ;\nWAIT (1=0) TIMEOUT,LBL[100] ;\nLBL[100:end] ;\n"
end
def test_pr_components
parse("foo := PR[1]\nfoo.x=5\nfoo.y=6\nfoo.z=7\nfoo.w=8\nfoo.p=9\nfoo.r=10\n")
assert_prog "PR[1,1:foo]=5 ;\nPR[1,2:foo]=6 ;\nPR[1,3:foo]=7 ;\nPR[1,4:foo]=8 ;\nPR[1,5:foo]=9 ;\nPR[1,6:foo]=10 ;\n"
end
#def test_pr_with_invalid_component_raises_error
# parse("foo := PR[1]\nfoo.bar=5\n")
# assert_raise(RuntimeError) do
# assert_prog ""
# end
#end
def test_simple_case_statement
parse("foo := R[1]\ncase foo\nwhen 1\njump_to @asdf\nend\n@asdf")
assert_prog %(SELECT R[1:foo]=1,JMP LBL[100] ;\nLBL[100:asdf] ;\n)
end
def test_simple_case_with_else_statement
parse("foo := R[1]\ncase foo\nwhen 1\njump_to @asdf\nelse\njump_to @ghjk\nend\n@asdf\n@ghjk")
assert_prog %(SELECT R[1:foo]=1,JMP LBL[100] ;
ELSE,JMP LBL[101] ;
LBL[100:asdf] ;
LBL[101:ghjk] ;\n)
end
def test_case_statement_with_two_whens
parse("foo := R[1]\ncase foo\nwhen 1\njump_to @asdf\nwhen 2\njump_to @ghjk\nend\n@asdf\n@ghjk")
assert_prog %(SELECT R[1:foo]=1,JMP LBL[100] ;
=2,JMP LBL[101] ;
LBL[100:asdf] ;
LBL[101:ghjk] ;\n)
end
def test_case_statement_with_three_whens
parse("foo := R[1]\ncase foo\nwhen 1\nbar()\nwhen 2\nbar()\nwhen 3\nbar()\nend")
assert_prog %(SELECT R[1:foo]=1,CALL BAR ;
=2,CALL BAR ;
=3,CALL BAR ;\n)
end
def test_case_statement_with_three_whens_and_else
parse("foo := R[1]\ncase foo\nwhen 1\nbar()\nwhen 2\nbar()\nwhen 3\nbar()\nelse\nbar()\nend")
assert_prog %(SELECT R[1:foo]=1,CALL BAR ;
=2,CALL BAR ;
=3,CALL BAR ;
ELSE,CALL BAR ;\n)
end
def test_can_use_simple_io_value_as_condition
parse("foo := UI[5]\n@top\njump_to @top if foo")
assert_prog "LBL[100:top] ;\nIF (UI[5:foo]),JMP LBL[100] ;\n"
end
def test_can_use_simple_io_value_as_condition_with_unless
parse("foo := UI[5]\n@top\njump_to @top unless foo")
assert_prog "LBL[100:top] ;\nIF (!UI[5:foo]),JMP LBL[100] ;\n"
end
def test_inline_program_call
parse("foo := UI[5]\nbar() unless foo")
assert_prog "IF (!UI[5:foo]),CALL BAR ;\n"
end
def test_constant_definition
parse("FOO := 5\nfoo := R[1]\nfoo = FOO")
assert_prog "R[1:foo]=5 ;\n"
end
def test_constant_definition_real
parse("PI := 3.14159\nfoo:= R[1]\nfoo = PI")
assert_prog "R[1:foo]=3.14159 ;\n"
end
def test_redefining_const_throws_error
assert_raise(RuntimeError) do
parse("PI := 3.14\nPI := 5")
assert_prog ""
end
end
def test_defining_const_without_caps_raises_error
parse("pi := 3.14")
assert_raise(RuntimeError) do
assert_prog ""
end
end
def test_using_argument_var
parse("foo := AR[1]\n@top\njump_to @top if foo==1")
assert_prog "LBL[100:top] ;\nIF (AR[1]=1),JMP LBL[100] ;\n"
end
def test_use_uframe_with_constant
parse("FOO := 1\nuse_uframe FOO")
assert_prog "UFRAME_NUM=1 ;\n"
end
def test_set_uframe_with_pr
parse("foo := PR[1]\nindirect('uframe',5)=foo")
assert_prog "UFRAME[5]=PR[1:foo] ;\n"
end
def test_set_uframe_with_constant
parse("foo := PR[1]\nBAR := 5\nindirect('uframe',BAR)=foo")
assert_prog "UFRAME[5]=PR[1:foo] ;\n"
end
def test_fanuc_set_uframe_with_reg
parse("foo := PR[1]\nbar := R[1]\nindirect('uframe',bar)=foo")
assert_prog "UFRAME[R[1:bar]]=PR[1:foo] ;\n"
end
def test_set_skip_condition
parse("foo := RI[1]\nset_skip_condition foo")
assert_prog "SKIP CONDITION RI[1:foo]=ON ;\n"
end
def test_set_skip_condition_with_bang
parse("foo := RI[1]\nset_skip_condition !foo")
assert_prog "SKIP CONDITION RI[1:foo]=OFF ;\n"
end
def test_skip_to
parse("p := P[1]\n@somewhere\nlinear_move.to(p).at(2000,'mm/s').term(0).skip_to(@somewhere)")
assert_prog "LBL[100:somewhere] ;\nL P[1:p] 2000mm/sec CNT0 Skip,LBL[100] ;\n"
end
def test_skip_to_with_pr
parse("p := P[1]\nlpos := PR[1]\n@somewhere\nlinear_move.to(p).at(2000,'mm/s').term(0).skip_to(@somewhere, lpos)")
assert_prog "LBL[100:somewhere] ;\nL P[1:p] 2000mm/sec CNT0 Skip,LBL[100],PR[1:lpos]=LPOS ;\n"
end
def test_label_comment_automatically_adds_a_comment_if_over_16_chars
parse("@foo_bar_foo_bar_foo")
assert_prog "LBL[100:foo_bar_foo_bar_] ;\n! foo_bar_foo_bar_foo ;\n"
end
def test_automatic_long_comment_wrapping
parse("# this is a really long comment so it should wrap")
assert_prog "! this is a really long comment ;\n! so it should wrap ;\n"
end
def test_turning_on_a_flag_requires_mixed_logic
parse("foo := F[1]\nturn_on foo")
assert_prog "F[1:foo]=(ON) ;\n"
end
def test_boolean_assignment
parse("foo := F[1]\nfoo = 1 && 1")
assert_prog "F[1:foo]=(1 AND 1) ;\n"
end
def test_simple_math
parse("foo := R[1]\nfoo=1+1")
assert_prog "R[1:foo]=1+1 ;\n"
end
def test_more_complicated_math
parse("foo := R[1]\nfoo=1+2+3")
assert_prog "R[1:foo]=(1+2+3) ;\n"
end
def test_operator_precedence
parse "foo := R[1]\nfoo=1+2*3"
assert_prog "R[1:foo]=(1+2*3) ;\n"
end
def test_expression_grouping
parse "foo := R[1]\nfoo=(1+2)*3"
assert_prog "R[1:foo]=((1+2)*3) ;\n"
end
def test_boolean_expression
parse "foo := F[1]\nfoo = 1 || 1 && 0"
assert_prog "F[1:foo]=(1 OR 1 AND 0) ;\n"
end
def test_bang
parse "foo := F[1]\nbar := F[2]\nfoo = !bar"
assert_prog "F[1:foo]=(!F[2:bar]) ;\n"
end
# issue #15 https://github.com/onerobotics/tp_plus/issues/15
def test_assign_output_to_ro
parse "foo := DO[1]\nbar := RO[1]\nfoo = bar"
assert_prog "DO[1:foo]=(RO[1:bar]) ;\n"
end
def test_assign_output_to_numreg_requires_no_parens
parse "foo := DO[1]\nbar := R[1]\nfoo = bar"
assert_prog "DO[1:foo]=R[1:bar] ;\n"
end
# issue #16 https://github.com/onerobotics/tp_plus/issues/16
def test_negative_assignment
parse "foo := R[1]\nfoo = -foo"
assert_prog "R[1:foo]=R[1:foo]*(-1) ;\n"
end
def test_bang_with_grouping
parse "foo := F[1]\nbar := F[2]\nbaz := F[3]\nfoo = foo || !(bar || baz)"
assert_prog "F[1:foo]=(F[1:foo] OR !(F[2:bar] OR F[3:baz])) ;\n"
end
def test_opposite_flag_in_simple_if
parse "foo := F[1]\nif foo\n# foo is true\nend"
assert_prog "IF (!F[1:foo]),JMP LBL[100] ;\n! foo is true ;\nLBL[100] ;\n"
end
def test_opposite_with_boolean_and
parse "foo := F[1]\nbar := F[2]\nbaz := F[3]\nif foo && bar && baz\n#foo, bar and baz are true\nend"
assert_prog "IF (!F[1:foo] OR !F[2:bar] OR !F[3:baz]),JMP LBL[100] ;\n! foo, bar and baz are true ;\nLBL[100] ;\n"
end
def test_opposite_with_boolean_or
parse "foo := F[1]\nbar := F[2]\nbaz := F[3]\nif foo || bar || baz\n#foo or bar or baz is true\nend"
assert_prog "IF (!F[1:foo] AND !F[2:bar] AND !F[3:baz]),JMP LBL[100] ;\n! foo or bar or baz is true ;\nLBL[100] ;\n"
end
def test_opposite_flag_in_simple_unless
parse "foo := F[1]\nunless foo\n# foo is false\nend"
assert_prog "IF (F[1:foo]),JMP LBL[100] ;\n! foo is false ;\nLBL[100] ;\n"
end
def test_inline_if_with_flag
parse "foo := F[1]\njump_to @end if foo\n@end"
assert_prog "IF (F[1:foo]),JMP LBL[100] ;\nLBL[100:end] ;\n"
end
def test_inline_unless_with_flag
parse "foo := F[1]\njump_to @end unless foo\n@end"
assert_prog "IF (!F[1:foo]),JMP LBL[100] ;\nLBL[100:end] ;\n"
end
def test_automatic_parens_on_boolean
parse "foo := F[1]\njump_to @end if foo || foo\n@end"
assert_prog "IF (F[1:foo] OR F[1:foo]),JMP LBL[100] ;\nLBL[100:end] ;\n"
end
def test_no_extra_parens_with_booleans
parse "foo := F[1]\njump_to @end if foo || foo || foo\n@end"
assert_prog "IF (F[1:foo] OR F[1:foo] OR F[1:foo]),JMP LBL[100] ;\nLBL[100:end] ;\n"
end
def test_assignment_as_bool_result
parse "foo := F[1]\nbar := R[1]\nfoo = bar == 1"
assert_prog "F[1:foo]=(R[1:bar]=1) ;\n"
end
def test_args_dont_get_comments
parse "foo := AR[1]\njump_to @end if foo == 1\n@end"
assert_prog "IF (AR[1]=1),JMP LBL[100] ;\nLBL[100:end] ;\n"
end
def test_indirect_position_assignment
parse "foo := PR[1]\nfoo = indirect('p',5)"
assert_prog "PR[1:foo]=P[5] ;\n"
end
# Issue #11
def test_indirect_gi
parse "foo := R[1]\nbar := R[2]\nfoo = indirect('gi', bar)"
assert_prog "R[1:foo]=GI[R[2:bar]] ;\n"
end
def test_indirect_indirect_position_assignment
parse "foo := PR[1]\nbar := R[1]\nfoo = indirect('p',bar)"
assert_prog "PR[1:foo]=P[R[1:bar]] ;\n"
end
def test_indirect_posreg_assignment
parse "foo := PR[1]\nfoo = indirect('pr',5)"
assert_prog "PR[1:foo]=PR[5] ;\n"
end
def test_add_posregs
parse "a := PR[1]\nb := PR[2]\nc := PR[3]\nd := PR[4]\na=b+c+d"
assert_prog "PR[1:a]=PR[2:b]+PR[3:c]+PR[4:d] ;\n"
end
def test_namespace
parse "namespace Foo\nbar := R[1]\nend\nFoo::bar = 5"
assert_prog "R[1:Foo bar]=5 ;\n"
end
def test_no_namespace_collision
parse "namespace Foo\nbar := R[1]\nend\nbar := R[2]\nbar = 2\nFoo::bar = 1"
assert_prog "R[2:bar]=2 ;\nR[1:Foo bar]=1 ;\n"
end
def test_namespace_constant_definition
parse "namespace Math\nPI := 3.14\nend\nfoo := R[1]\nfoo = Math::PI"
assert_prog "R[1:foo]=3.14 ;\n"
end
def test_namespace_swallows_everything
parse "namespace Foo\n# this is a comment\n#this is another comment\nend"
assert_prog ""
end
def test_nested_namespace
parse %(namespace Foo
bar := R[1]
namespace Bar
baz := R[2]
end
end
Foo::bar = 1
Foo::Bar::baz = 2)
assert_prog "R[1:Foo bar]=1 ;\nR[2:Foo Bar baz]=2 ;\n"
end
def test_load_environment
environment = "foo := R[1]\nbar := R[2]\n#asdf\n#asdf"
@interpreter.load_environment(environment)
parse "foo = 5"
assert_prog "R[1:foo]=5 ;\n"
assert_equal 1, @interpreter.source_line_count
end
def test_load_environment_only_saves_definitions_etc
environment = "foo := R[1]\nbar := R[2]\n#asdf\n#asdf\nfoo=3"
@interpreter.load_environment(environment)
parse "foo = 5"
assert_prog "R[1:foo]=5 ;\n"
assert_equal 1, @interpreter.source_line_count
end
def test_bad_environment
assert_raise(TPPlus::Parser::ParseError) do
@interpreter.load_environment("asdf")
end
end
def test_inline_conditional_with_namespaced_var
parse "namespace Foo\nbar := DI[1]\nend\njump_to @end unless Foo::bar\n@end"
assert_prog "IF (!DI[1:Foo bar]),JMP LBL[100] ;\nLBL[100:end] ;\n"
end
def test_namespaced_var_as_condition
parse "namespace Foo\nbar := DI[1]\nend\nif Foo::bar\n# bar is on\nend"
assert_prog "IF (!DI[1:Foo bar]),JMP LBL[100] ;\n! bar is on ;\nLBL[100] ;\n"
end
def test_reopen_namespace
parse "namespace Foo\nbar := R[1]\nend\nnamespace Foo\nbaz := R[2]\nend\nFoo::bar = 1\nFoo::baz = 2"
assert_prog "R[1:Foo bar]=1 ;\nR[2:Foo baz]=2 ;\n"
end
def test_eval
parse %(eval "R[1]=5")
assert_prog "R[1]=5 ;\n"
end
def test_multiline_eval
parse %(eval "R[1]=5 ;\nR[2]=3")
assert_prog "R[1]=5 ;\nR[2]=3 ;\n"
end
def test_namespaced_position_reg_component
parse "namespace Fixture\npick_offset := PR[1]\nend\nFixture::pick_offset.x = 10"
assert_prog "PR[1,1:Fixture pick_offset]=10 ;\n"
end
def test_inline_program_call_two
parse "foo := R[1]\nbar() if foo >= 5"
assert_prog "IF R[1:foo]>=5,CALL BAR ;\n"
end
def test_forlooop
parse "foo := R[1]\nfor foo in (1 to 10)\n# bar\nend"
assert_prog "FOR R[1:foo]=1 TO 10 ;\n! bar ;\nENDFOR ;\n"
end
def test_forloop_with_vars
parse "foo := R[1]\nmin := R[2]\nmax := R[3]\nfor foo in (min to max)\n#bar\nend"
assert_prog "FOR R[1:foo]=R[2:min] TO R[3:max] ;\n! bar ;\nENDFOR ;\n"
end
def test_indirect_flag
parse "foo := R[1]\nturn_on indirect('f',foo)"
assert_prog "F[R[1:foo]]=(ON) ;\n"
end
def test_indirect_flag_condition
parse "foo := R[1]\njump_to @end if indirect('f',foo)\n@end"
assert_prog "IF (F[R[1:foo]]),JMP LBL[100] ;\nLBL[100:end] ;\n"
end
def test_indirect_flag_condition_not_inline
parse "foo := R[1]\nif indirect('f',foo)\n# bar\nend"
assert_prog "IF (!F[R[1:foo]]),JMP LBL[100] ;\n! bar ;\nLBL[100] ;\n"
end
def test_indirect_unless_flag_condition
parse "foo := R[1]\njump_to @end unless indirect('f',foo)\n@end"
assert_prog "IF (!F[R[1:foo]]),JMP LBL[100] ;\nLBL[100:end] ;\n"
end
def test_indirect_flag_with_if_bang
parse "foo := R[1]\njump_to @end if !indirect('f',foo)\n@end"
assert_prog "IF (!F[R[1:foo]]),JMP LBL[100] ;\nLBL[100:end] ;\n"
end
def test_while_loop
parse "foo := R[1]\nwhile foo < 10\n# bar\nend"
assert_prog "LBL[100] ;\nIF R[1:foo]>=10,JMP LBL[101] ;\n! bar ;\nJMP LBL[100] ;\nLBL[101] ;\n"
end
def test_while_with_not_flag
parse "foo := F[1]\nwhile !foo\n#bar\nend"
assert_prog "LBL[100] ;\nIF (F[1:foo]),JMP LBL[101] ;\n! bar ;\nJMP LBL[100] ;\nLBL[101] ;\n"
end
def test_while_with_flag
parse "foo := F[1]\nwhile foo\n#bar\nend"
assert_prog "LBL[100] ;\nIF (!F[1:foo]),JMP LBL[101] ;\n! bar ;\nJMP LBL[100] ;\nLBL[101] ;\n"
end
def test_timer_start
parse "foo := TIMER[1]\nstart foo"
assert_prog "TIMER[1]=START ;\n"
end
def test_timer_reset
parse "foo := TIMER[1]\nreset foo"
assert_prog "TIMER[1]=RESET ;\n"
end
def test_timer_stop
parse "foo := TIMER[1]\nstop foo"
assert_prog "TIMER[1]=STOP ;\n"
end
def test_timer_restart
parse "foo := TIMER[1]\nrestart foo"
assert_prog "TIMER[1]=STOP ;\nTIMER[1]=RESET ;\nTIMER[1]=START ;\n"
end
def test_indirect_timer
parse "foo := R[1]\nfoo = indirect('timer', 3)"
assert_prog "R[1:foo]=TIMER[3] ;\n"
end
def test_indirect_indirect_timer
parse "foo := R[1]\nfoo = indirect('timer', foo)"
assert_prog "R[1:foo]=TIMER[R[1:foo]] ;\n"
end
def test_start_indirect_timer
parse "start indirect('timer', 3)"
assert_prog "TIMER[3]=START ;\n"
end
def test_start_indirect_indirect_timer
parse "foo := R[1]\nstart indirect('timer', foo)"
assert_prog "TIMER[R[1:foo]]=START ;\n"
end
def test_position_data_does_not_output_with_eval
parse %(position_data
{
'positions': [
{
'id': 1,
'comment': "test",
'mask' : [{
'group': 1,
'uframe': 1,
'utool': 1,
'config': {
'flip': true,
'up': true,
'top': false,
'turn_counts': [-1,0,1]
},
'components': {
'x': -50.0,
'y': 0.0,
'z': 0.0,
'w': 0.0,
'p': 0.0,
'r': 0.0
}
}]
}
]
}
end)
assert_prog ""
end
def test_position_data_populates_interpreter_position_data
parse %(position_data
{
'positions': [
{
'id': 1,
'comment': "test",
'mask': [{
'group': 1,
'uframe': 1,
'utool': 1,
'config': {
'flip': true,
'up': true,
'top': false,
'turn_counts': [0,0,0]
},
'components': {
'x': 0.0,
'y': 0.0,
'z': 0.0,
'w': 0.0,
'p': 0.0,
'r': 0.0
}
}]
}
]
}
end)
assert_prog ""
assert_equal 1, @interpreter.position_data[:positions].length
end
def test_throws_a_fault_if_position_data_invalid
parse %(position_data
{
'positions' : "asdf"
}
end)
assert_raise(RuntimeError) do
@interpreter.eval
end
end
def test_outputs_position_data_correctly
parse %(position_data
{
'positions': [
{
'id': 1,
'comment': "test",
'mask' : [{
'group': 1,
'uframe': 1,
'utool': 1,
'config': {
'flip': true,
'up': true,
'top': true,
'turn_counts': [0,0,0]
},
'components': {
'x': 0.0,
'y': 0.0,
'z': 0.0,
'w': 0.0,
'p': 0.0,
'r': 0.0
}
}]
}
]
}
end)
assert_prog ""
assert_equal %(P[1:"test"]{
GP1:
UF : 1, UT : 1, CONFIG : 'F U T, 0, 0, 0',
X = 0.0 mm, Y = 0.0 mm, Z = 0.0 mm,
W = 0.0 deg, P = 0.0 deg, R = 0.0 deg
};\n), @interpreter.pos_section
end
def test_outputs_position_data_correctly_with_two_positions
parse %(position_data
{
'positions': [
{
'id': 1,
'comment': "test",
'mask': [{
'group': 1,
'uframe': 1,
'utool': 1,
'config': {
'flip': true,
'up': true,
'top': true,
'turn_counts': [0,0,0]
},
'components': {
'x': 0.0,
'y': 0.0,
'z': 0.0,
'w': 0.0,
'p': 0.0,
'r': 0.0
}
}]
},
{
'id': 2,
'comment': "test2",
'mask': [{
'group': 1,
'uframe': 1,
'utool': 1,
'config': {
'flip': true,
'up': true,
'top': true,
'turn_counts': [0,0,0]
},
'components': {
'x': 0.0,
'y': 0.0,
'z': 0.0,
'w': 0.0,
'p': 0.0,
'r': 0.0
}
}]
}
]
}
end)
assert_prog ""
assert_equal %(P[1:"test"]{
GP1:
UF : 1, UT : 1, CONFIG : 'F U T, 0, 0, 0',
X = 0.0 mm, Y = 0.0 mm, Z = 0.0 mm,
W = 0.0 deg, P = 0.0 deg, R = 0.0 deg
};
P[2:"test2"]{
GP1:
UF : 1, UT : 1, CONFIG : 'F U T, 0, 0, 0',
X = 0.0 mm, Y = 0.0 mm, Z = 0.0 mm,
W = 0.0 deg, P = 0.0 deg, R = 0.0 deg
};\n), @interpreter.pos_section
end
def test_simple_pulse
parse "foo := DO[1]\npulse foo"
assert_prog "DO[1:foo]=PULSE ;\n"
end
def test_pulse_for_seconds
parse "foo := DO[1]\npulse(foo,5,'s')"
assert_prog "DO[1:foo]=PULSE,5.0sec ;\n"
end
def test_pulse_for_ms
parse "foo := DO[1]\npulse(foo,500,'ms')"
assert_prog "DO[1:foo]=PULSE,0.5sec ;\n"
end
def test_indirect_numreg
parse "foo := R[1]\nindirect('r',foo) = 5"
assert_prog "R[R[1:foo]]=5 ;\n"
end
def test_raise
parse "my_alarm := UALM[1]\nraise my_alarm"
assert_prog "UALM[1] ;\n"
end
def test_indirect_raise
parse "raise indirect('ualm',1)"
assert_prog "UALM[1] ;\n"
end
def test_indirect_indirect_raise
parse "foo := R[1]\nraise indirect('ualm',foo)"
assert_prog "UALM[R[1:foo]] ;\n"
end
def test_namespaced_pr_component_assignment
parse "namespace Foo\nbar := PR[1]\nend\nFoo::bar.x = 10"
assert_prog "PR[1,1:Foo bar]=10 ;\n"
end
def test_namespaced_pr_component_plus_equals
parse "namespace Foo\nbar := PR[1]\nend\nFoo::bar.x += 10"
assert_prog "PR[1,1:Foo bar]=PR[1,1:Foo bar]+10 ;\n"
end
def test_namespaced_pr_component_in_expression
parse "namespace Foo\nbar := PR[1]\nend\nFoo::bar.x += Foo::bar.x * 10 \n"
assert_prog "PR[1,1:Foo bar]=(PR[1,1:Foo bar]+PR[1,1:Foo bar]*10) ;\n"
end
def test_namespaced_pr_assignment_then_expression
parse "namespace Foo\nbar := PR[1]\nend\nbaz := P[1]\nFoo::bar = baz\nFoo::bar.x += 5"
assert_prog "PR[1:Foo bar]=P[1:baz] ;\nPR[1,1:Foo bar]=PR[1,1:Foo bar]+5 ;\n"
end
def test_wait_until_does_not_get_inlined
parse "foo := DO[1]\nunless foo\nwait_until(foo)\nend"
assert_prog "IF (DO[1:foo]),JMP LBL[100] ;\nWAIT (DO[1:foo]) ;\nLBL[100] ;\n"
end
def test_wait_for_does_not_get_inlined
parse "foo := DO[1]\nunless foo\nwait_for(1,'s')\nend"
assert_prog "IF (DO[1:foo]),JMP LBL[100] ;\nWAIT 1.00(sec) ;\nLBL[100] ;\n"
end
def test_inline_conditional_does_not_get_inlined
parse "foo := DO[1]\nif foo\nturn_off foo if foo\nend"
assert_prog "IF (!DO[1:foo]),JMP LBL[100] ;\nIF (DO[1:foo]),DO[1:foo]=(OFF) ;\nLBL[100] ;\n"
end
def test_run
parse "run foo()"
assert_prog "RUN FOO ;\n"
end
def test_tp_ignore_pause
parse "TP_IGNORE_PAUSE = true"
assert_prog ""
assert @interpreter.header_data[:ignore_pause]
end
def test_tp_subtype
parse "TP_SUBTYPE = 'macro'"
assert_prog ""
assert_equal :macro, @interpreter.header_data[:subtype]
end
def test_tp_comment
parse %(TP_COMMENT = "foo")
assert_prog ""
assert_equal "foo", @interpreter.header_data[:comment]
end
def test_tp_groupmask
parse %(TP_GROUPMASK = "*,*,*,*,*")
assert_prog ""
assert_equal "*,*,*,*,*", @interpreter.header_data[:group_mask]
end
def test_mixed_logic_or
parse %(foo := DI[1]\nbar := DI[2]\nif foo || bar\n# do something\nend)
assert_prog "IF (!DI[1:foo] AND !DI[2:bar]),JMP LBL[100] ;\n! do something ;\nLBL[100] ;\n"
end
def test_flag_assignment_always_gets_parens
parse %(foo := F[1]\nbar := DI[2]\nfoo = bar)
assert_prog "F[1:foo]=(DI[2:bar]) ;\n"
end
def test_tool_offset
parse %(p := P[1]\ntoff := PR[1]\nlinear_move.to(p).at(2000,'mm/s').term(0).tool_offset(toff))
assert_prog "L P[1:p] 2000mm/sec CNT0 Tool_Offset,PR[1:toff] ;\n"
end
def test_wait_for_digit
parse %(wait_for(1,'s'))
assert_prog "WAIT 1.00(sec) ;\n"
end
def test_wait_for_real
parse %(wait_for(0.5,'s'))
assert_prog "WAIT .50(sec) ;\n"
end
def test_wait_for_real_const
parse %(FOO := 0.5\nwait_for(FOO,'s'))
assert_prog "WAIT .50(sec) ;\n"
end
def test_negative_numbers_have_parens
parse %(foo := R[1]\njump_to @end if foo > -1\njump_to @end if foo > -5.3\n@end)
assert_prog "IF R[1:foo]>(-1),JMP LBL[100] ;\nIF R[1:foo]>(-5.3),JMP LBL[100] ;\nLBL[100:end] ;\n"
end
def test_modulus
parse %(foo := R[1]\nfoo = 5 % 2)
assert_prog "R[1:foo]=5 MOD 2 ;\n"
end
def test_assignment_to_sop
parse %(foo := DO[1]\nbar := SO[1]\nfoo = bar)
assert_prog "DO[1:foo]=(SO[1:bar]) ;\n"
end
def test_assignment_to_di
parse %(foo := DO[1]\nbar := DI[1]\nfoo = bar)
assert_prog "DO[1:foo]=(DI[1:bar]) ;\n"
end
def test_string_register_definition
parse %(foo := SR[1]\nbar := SR[2]\nfoo = bar)
assert_prog "SR[1:foo]=SR[2:bar] ;\n"
end
def test_string_argument
parse %(foo('bar'))
assert_prog "CALL FOO('bar') ;\n"
end
def test_pause
parse %(pause)
assert_prog "PAUSE ;\n"
end
def test_abort
parse %(abort)
assert_prog "ABORT ;\n"
end
def test_div
parse %(foo := R[1]\nfoo = 1 DIV 5)
assert_prog "R[1:foo]=1 DIV 5 ;\n"
end
def test_conditional_equals
parse("foo := R[1]\nfoo2 := R[2]\nif foo == foo2\nfoo = 1\nfoo2 = 2\nend")
assert_prog "IF R[1:foo]<>R[2:foo2],JMP LBL[100] ;\nR[1:foo]=1 ;\nR[2:foo2]=2 ;\nLBL[100] ;\n"
end
def test_if_statement_multiple_arguments
parse("foo := R[1]\nfoo2 := R[2]\nif foo == 1 && foo2 == 2\nfoo = 1\nfoo2 = 2\nend")
assert_prog "IF (R[1:foo]<>1 OR R[2:foo2]<>2),JMP LBL[100] ;\nR[1:foo]=1 ;\nR[2:foo2]=2 ;\nLBL[100] ;\n"
end
def test_parse_error_on_invalid_term
assert_raise do
parse("foo := PR[1]\nlinear_move.to(foo).at(2000, 'mm/s').term(-2)")
end
end
def test_acc_zero
parse("foo := PR[1]\nlinear_move.to(foo).at(2000, 'mm/s').term(0).acc(0)")
assert_prog "L PR[1:foo] 2000mm/sec CNT0 ACC0 ;\n"
end
def test_acc_150
parse("foo := PR[1]\nlinear_move.to(foo).at(2000, 'mm/s').term(0).acc(150)")
assert_prog "L PR[1:foo] 2000mm/sec CNT0 ACC150 ;\n"
end
def test_acc_with_constant
parse("foo := PR[1]\nBAR := 50\nlinear_move.to(foo).at(2000, 'mm/s').term(0).acc(BAR)")
assert_prog "L PR[1:foo] 2000mm/sec CNT0 ACC50 ;\n"
end
def test_acc_with_var
parse("foo := PR[1]\nbar := R[1]\nlinear_move.to(foo).at(2000, 'mm/s').term(0).acc(bar)")
assert_prog "L PR[1:foo] 2000mm/sec CNT0 ACC R[1:bar] ;\n"
end
def test_term_fine_with_constant
parse("foo := PR[1]\nTERM := -1\nlinear_move.to(foo).at(2000, 'mm/s').term(TERM)")
assert_prog "L PR[1:foo] 2000mm/sec FINE ;\n"
end
def test_term_cnt_with_constant
parse("foo := PR[1]\nTERM := 100\nlinear_move.to(foo).at(2000, 'mm/s').term(TERM)")
assert_prog "L PR[1:foo] 2000mm/sec CNT100 ;\n"
end
def test_pr_components_groups
parse("foo := PR[1]\nfoo.group(1).y=5\n")
assert_prog "PR[GP1:1,2:foo]=5 ;\n"
end
def test_position_data_with_mask
parse %(position_data
{
'positions' : [
{
'id' : 1,
'mask' : [{
'group' : 1,
'uframe' : 5,
'utool' : 2,
'config' : {
'flip' : false,
'up' : true,
'top' : true,
'turn_counts' : [0,0,0]
},
'components' : {
'x' : -.590,
'y' : -29.400,
'z' : 1304.471,
'w' : 78.512,
'p' : 89.786,
'r' : -11.595
}
},
{
'group' : 2,
'uframe' : 5,
'utool' : 2,
'components' : {
'J1' : 0.00
}
}]
}
]
}
end)
assert_prog ""
#output = @interpreter.pos_section
#output = output
assert_equal 1, @interpreter.position_data[:positions].length
end
def test_conditional_equals_minus_one
parse("foo := R[1]\nfoo2 := R[2]\nif foo == (foo2-1)\nfoo = 1\nfoo2 = 2\nend")
assert_prog "IF (R[1:foo]<>(R[2:foo2]-1)),JMP LBL[100] ;\nR[1:foo]=1 ;\nR[2:foo2]=2 ;\nLBL[100] ;\n"
end
def test_conditional_equals_minus_and
parse("foo := R[1]\nfoo2 := R[2]\nif foo >= (foo2-1) && foo <= (foo2+1) \nfoo = 1\nfoo2 = 2\nend")
assert_prog "IF (R[1:foo]<(R[2:foo2]-1) OR R[1:foo]>(R[2:foo2]+1)),JMP LBL[100] ;\nR[1:foo]=1 ;\nR[2:foo2]=2 ;\nLBL[100] ;\n"
end
def test_inline_conditional_equals_minus_and
parse("foo := R[1]\nfoo2 := R[2]\nif foo >= (foo2-1) && foo <= (foo2+1) \nfoo2 = 2\nend")
assert_prog "IF (R[1:foo]>=(R[2:foo2]-1) AND R[1:foo]<=(R[2:foo2]+1)),R[2:foo2]=(2) ;\n"
end
def test_address
parse("a := AR[1]
b := DI[2]
c := R[3]
d := P[4]
e := PR[5]
f := SR[6]
g := TIMER[7]
h := UALM[8]
i := VR[9]
TEST(&a,&b,&c,&d,&e,&f,&g,&h,&i)")
assert_prog "CALL TEST(1,2,3,4,5,6,7,8,9) ;\n"
end
def test_address_assignment
parse("foo := R[1]
foo = &foo")
assert_prog "R[1:foo]=1 ;\n"
end
def test_invalid_address_throws_error
parse("foo := R[1]\nfoo = &bar")
assert_raise(RuntimeError) do
assert_prog ""
end
end
def test_lpos
parse "foo := PR[1]\nget_linear_position(foo)"
assert_prog "PR[1:foo]=LPOS ;\n"
end
def test_jpos
parse "foo := PR[1]\nget_joint_position(foo)"
assert_prog "PR[1:foo]=JPOS ;\n"
end
def test_return
parse "return"
assert_prog "END ;\n"
end
# issue #18 https://github.com/onerobotics/tp_plus/issues/18
def test_mixed_logic_with_multiple_conditions_and_not_operator
parse "foo := RI[1]\nbar := RI[2]\nif !foo && !bar\n# true\nend"
assert_prog "IF (RI[1:foo] OR RI[2:bar]),JMP LBL[100] ;\n! true ;\nLBL[100] ;\n"
end
end