Post by Tom Not logged in on Dec 3, 2005 6:35:20 GMT -5
Someone asked me how to write a compiler today, which is a huge topic (just ask Neophyte ), but did eventually lead me to writing this little demonstration interpreted scripting language.
It came out to about 300 lines and about 8K, which means it will fit in a forum post, so I thought I'd share.
Cheers,
-Tom
It came out to about 300 lines and about 8K, which means it will fit in a forum post, so I thought I'd share.
Cheers,
-Tom
'''''''''''''''''''''''''''''
' Parsing program variables '
'''''''''''''''''''''''''''''
' The current line number and column number being executed
dim line, col
dim lineCount ' Total # of lines in the program
' The text of that line
dim line$
' The current word being executed
dim word$
' The current character being parsed
dim char$
'''''''''''''''''''''''''''''
' Scripting state variables '
'''''''''''''''''''''''''''''
' Our simple language will have 26 variables, with names from 'a' - 'z'.
' We will store each one's value in an array, so variables#(1) stores a's value, variables#(2) stores b's value.
dim variables#(26)
' Keep track of the index of the variable we are assigning to
dim variable
' When we calculate expressions like 1 + 2 + 3 or a - b, we will keep track of the current
' value in value#, and the running total in value2#
dim value#, value2#
' Other things we need to keep track of while we're executing statements
dim operation$ ' The current + or - operation (used when evaluating expressions)
dim instruction$ ' The current "if" instruction
dim ifResult ' The result of the current "if" instruction
'''''''''''''''''''''
' Working variables '
'''''''''''''''''''''
dim i
'''''''''''
' Program '
'''''''''''
' Our programming language is quite simple
' Every instruction is on its own line
' The program ends with a line that contains just "."
' We can assign numbers to our variables (a = 3)
' We can calculate expressions with + or - (a = b + 3 - c)
' We can print out the results (print a + b)
' We can skip the next line if a calculation is positive, negative or zero (ifzero a - b)
' We can goto a line # (goto 4)
' The following program calculates and displays the fibonacci sequence
data "a = 0"
data "b = 1"
data "print b"
data "c = b"
data "b = b + a"
data "a = c"
data "ifpositive 10000 - b"
data "goto 3"
data "."
' Count lines in program
while line$ <> "."
lineCount = lineCount + 1
read line$
wend
' Read in program
dim program$(lineCount)
reset
for i = 1 to lineCount
read program$(i)
next
'''''''''''''''
' Subroutines '
'''''''''''''''
goto Start
' Text parsing
' The idea is to break the program into individual words, so we can interpret each word individually.
' Everytime we call ReadNextWord, it will return with the next word stored in word$
ReadNextChar:
if col <= len(line$) then
' Read and return character from line
char$ = mid$(line$, col, 1)
col = col + 1
else
' Found end of line
' Return a return character
char$ = chr$(13)
' Load in next line
line = line + 1
line$ = program$(line)
col = 1
endif
return
ReadNextWord:
' End of program found?
if line$ = "." then
word$ = "."
return
endif
' Skip spaces
while char$ = " "
gosub ReadNextChar
wend
' Newline found?
if char$ = chr$(13) then
word$ = char$
gosub ReadNextChar
return
endif
' Read in word
word$ = ""
while char$ <> " " and char$ <> chr$(13)
word$ = word$ + char$
gosub ReadNextChar
wend
return
' Script interpretation
DoPrint:
' Skip "print" keyword
gosub ReadNextWord
' Calculate expression
gosub DoExpression
' Print the result on screen
printr value#
return
DoIf:
' Save instruction
instruction$ = word$
' Skip it
gosub ReadNextWord
' Calculate expression
gosub DoExpression
' Calculate result of "if"
if instruction$ = "ifpositive" then
ifresult = value# > 0
elseif instruction$ = "ifnegative" then
ifresult = value# < 0
elseif instruction$ = "ifzero" then
ifresult = value# = 0
endif
' If "ifresult" is true, we simply continue, and the next line will be executed.
' Otherwise if it is false, we skip the next line
if not ifresult then
line = line + 1
line$ = program$(line)
col = 1
char$ = " "
word$ = chr$(13)
endif
return
DoGoto:
' Skip goto
gosub ReadNextWord
' Calculate expression
gosub DoExpression
' Check expression is a valid line number
if int(value#) < 1 or int(value#) > lineCount then
print "Unknown line: " + int(value#)
goto Error
endif
' Goto the start of that line
line = value#
line$ = program$(line)
col = 1
char$ = " "
word$ = chr$(13)
return
DoAssignment:
' Find the variable. This is the one being assigned to.
' So if the variable is "a", we will store 1 in 'variable'. If it's "b" we will store 2 etc.
variable = asc(word$) - asc("a") + 1
' Expect it to be followed by an = sign
gosub ReadNextWord
if word$ <> "=" then
printr "Expected ="
goto Error
endif
' Calculate the expression
gosub ReadNextWord
gosub DoExpression
' Assign it to the variable
variables#(variable) = value#
return
DoExpression:
' This calculates the current expression, and stores the result in value#
' Read in a value
gosub DoValue
value2# = value#
' Look for "+" or "-" signs
while word$ = "+" or word$ = "-"
operation$ = word$
' Skip +/- sign
gosub ReadNextWord
' Read value into value#
gosub DoValue
' Add/subtract from total
if operation$ = "+" then
value2# = value2# + value#
else
value2# = value2# - value#
endif
wend
' Return result in value#
value# = value2#
return
DoValue:
' Is it a variable?
if len(word$) = 1 and word$ >= "a" and word$ <= "z" then
gosub DoVariable
' Or is it a number?
elseif left$(word$, 1) >= "0" and left$(word$, 1) <= "9" then
gosub DoNumber
else
printr "Expected variable or number"
goto Error
endif
return
DoVariable:
' Read in the value of the variable corresponding to word$
value# = variables#(asc(word$) - asc("a") + 1)
gosub ReadNextWord
return
DoNumber:
' Convert word$ into a number
value# = val(word$)
gosub ReadNextWord
return
Error:
printr "Error! Keyword " + word$ + " line " + line + ", col " + col
printr line$
for i = 1 to col - 1: print" ": next
print "^"
end
Start:
' Start at line 1
line = 1: col = 1: line$ = program$(1): char$ = " "
' Read in the first word
gosub ReadNextWord
' Main loop
while line$ <> "."
' Check if word$ is a recognised command
if word$ = "print" then
gosub DoPrint
elseif word$ = "ifpositive" or word$ = "ifnegative" or word$ = "ifzero" then
gosub DoIf
elseif word$ = "goto" then
gosub DoGoto
' Check if word is a letter.
' If so, we will assume it is an a = b type of assignment.
elseif len(word$) = 1 and word$ >= "a" and word$ <= "z" then
gosub DoAssignment
else
' Don't know what this is. Output an error.
goto Error
endif
' Expect end-of-line after each command
if word$ <> chr$(13) then
printr "Expected end-of-line"
goto Error
endif
gosub ReadNextWord
wend
' Reached end of program
printr "Done"