//mparse.bcpl

external [
// OS
	Resets; Wss; Wns; Puts; MoveBlock

// Defined here
	ParseAddress; ParseTextList
]

manifest[
	BadToken = 1
	SymbToken = 2
	NumbToken = 3
	LimitToken = 4
	BlankToken = 5
	CommaToken = 6
	DotToken = 7
	OtherToken = 8
	]


let ParseAddress(TV, lvX, PV, lvY) = valof
[	unless ParseSymbol(TV, lvX, PV, lvY) then resultis false
	unless ParseSign(TV, lvX, PV, lvY) then resultis false
	unless ParseNumb(TV, lvX, PV, lvY) then resultis false
	resultis true
]


and ParseSymbol(TV, lvX, PV, lvY) = valof
[	let TSize = nil
	SkipBlankToken(TV, lvX)
	switchon ChkToken(TV, rv lvX, lv TSize) into
	[
case SymbToken:
case DotToken:	resultis CopyToken(TV, lvX, PV, lvY, TSize)
case BadToken:	resultis false
default:	resultis CopyToken(TV, lvX, PV, lvY, 0)
	]
]


and ParseSign(TV, lvX, PV, lvY) = valof
[	let TSize = nil
	SkipBlankToken(TV, lvX)
	switchon ChkToken(TV, rv lvX, lv TSize) into
	[
case OtherToken: resultis CopyToken(TV,lvX,PV,lvY,
	  ((TV!(rv lvX) eq $+) logor (TV!(rv lvX) eq $-)) ? 1,0)
case BadToken: resultis false
default:  resultis CopyToken(TV, lvX, PV, lvY, 0)
	]
]


and ParseNumb(TV, lvX, PV, lvY) = valof
[	let TSize = nil
	SkipBlankToken(TV, lvX)
	switchon ChkToken(TV, rv lvX, lv TSize) into
	[
case NumbToken: resultis CopyToken(TV, lvX, PV, lvY, TSize)
case BadToken: resultis false
default: resultis CopyToken(TV, lvX, PV, lvY, 0)
	]
]


and ParseText(TV, lvX, PV, lvY) = valof
[	SkipBlankToken(TV, lvX)
	for I = rv lvX to TV!0 do
	  if TV!I eq $, then
	  [ let Size = I - rv lvX
	    resultis CopyToken(TV, lvX, PV, lvY, Size)
	  ]
	let Size = (TV!0) - rv lvX + 1
	resultis CopyToken(TV, lvX, PV, lvY, Size)
]


and ParseTextList(TV, lvX, Pv, lvY) = valof
[	while rv lvX le TV!0 do
	[ if not ParseText(TV, lvX, Pv, lvY) then resultis false
	  if rv lvX le TV!0 then rv lvX = rv lvX + 1 // skip comma
	]
	resultis true
]


and SkipBlankToken(TV, lvX) be
[	let TSize = nil
	unless ChkToken(TV, rv lvX, lv TSize) eq BlankToken then return
	rv lvX = rv lvX + TSize
]


and CopyToken(TV, lvX, PV, lvY, Size) = valof
[	let Y = rv lvY
	let X = rv lvX
	if Y + Size > PV!0 then resultis false
	PV!Y = Size
	MoveBlock(PV+Y+1,TV+X,Size)
	rv lvX = X + Size
	rv lvY = Y + Size + 1
	resultis true
]


and ChkToken(TV, X, lvSize) = valof
[	let C = TV!X
	let L = TV!0
	let Kind = BadToken
	let Size = 0
	let J = nil
	test X>TV!0
	ifso Kind = LimitToken
	ifnot test  ($a le C & C le $z) % ($A le C & C le $Z) % (C eq $8)
		% (C eq $9)
	  ifso Kind = SymbToken
	  ifnot test ($0 le C logand C le $7)
	    ifso Kind = NumbToken
	    ifnot test C eq $,
	      ifso Kind = CommaToken
	      ifnot test C eq $ 
		ifso Kind = BlankToken
		ifnot Kind = OtherToken
	switchon Kind into
	[
case SymbToken:
case NumbToken:
	  J = X + 1
	  while J le L do
	  [ let Ch = TV!J
	    if Ch eq $  % Ch eq $, then break
	    if (Ch eq $+ % Ch eq $-) & ((J+1) le L) then
		if TV!(J+1) < $8 then break
	    if Kind eq NumbToken
	      then unless ($0 le Ch logand Ch le $7)
		do Kind = SymbToken
	    if Ch eq $. then Kind = DotToken
	    J = J + 1
	  ]
	  Size = J - X; endcase
case LimitToken: Size = 0; endcase
case CommaToken:
case OtherToken: Size = 1; endcase
case BlankToken:
	  J = X + 1
	  while J le L do [ if TV!J ne $   then break; J = J + 1]
	  Size = J - X; endcase
	]
	rv lvSize = Size
	resultis Kind
]