Skip to content

Commit

Permalink
feat: parse for loops v0
Browse files Browse the repository at this point in the history
  • Loading branch information
Houcine EL ADDALI committed Dec 18, 2023
1 parent 7d7b92d commit 97fa1e4
Show file tree
Hide file tree
Showing 10 changed files with 445 additions and 256 deletions.
57 changes: 57 additions & 0 deletions internal/AST/ast_imp.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,63 @@ func (infixExp *InfixExpression) ToString() string {

func (infixExp *InfixExpression) expressionNode() {}

/*
For loop expressions Node
for def i=0;i<7;i=i+1 {
}
*/
type ForLoopExpression struct {
Token token.Token // the 'for' token idencate for loop starting point
InitStm Statement // the initializaiton stm
Condition Expression // loop condition
PostIteration Expression // the post iteration expression
Body *BlockStm // loop body that would be executed
}

func (forExp *ForLoopExpression) TokenLiteral() string {
return forExp.Token.Value
}
func (forExp *ForLoopExpression) expressionNode() {}
func (forExp *ForLoopExpression) ToString() string {

var bf bytes.Buffer
bf.WriteString(forExp.TokenLiteral())
bf.WriteString(" (")
bf.WriteString(forExp.InitStm.ToString())
bf.WriteString("; ")
bf.WriteString(forExp.Condition.ToString())
bf.WriteString("; ")
bf.WriteString(forExp.PostIteration.ToString())
bf.WriteString(" )")

bf.WriteString(forExp.Body.ToString())
return bf.String()
}

/*
PostfixExpression Node
*/

type PostfixExpression struct {
Token token.Token // token "INC , DEC (++,--)"
Operator string
Left Expression
}

func (postfixExp *PostfixExpression) expressionNode() {}
func (postfixExp *PostfixExpression) TokenLiteral() string {
return postfixExp.Token.Value
}

func (postfixExp *PostfixExpression) ToString() string {
var bf bytes.Buffer
bf.WriteRune('(')
bf.WriteString(postfixExp.Left.ToString())
bf.WriteString(postfixExp.Operator)
bf.WriteRune(')')
return bf.String()
}

/*
- If expression Nodes
implements node and expression Interfaces
Expand Down
6 changes: 3 additions & 3 deletions internal/AST/ast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ func TestToString(t *testing.T) {
program := &Program{
Statements: []Statement{
&DefStatement{
Token: token.NewToken(token.DEF, "def"),
Token: token.CreateToken(token.DEF, "def"),
Name: &Identifier{
Token: token.NewToken(token.IDENTIFIER, "var1"),
Token: token.CreateToken(token.IDENTIFIER, "var1"),
Value: "var1",
},
Value: &Identifier{
Token: token.NewToken(token.IDENTIFIER, "var2"),
Token: token.CreateToken(token.IDENTIFIER, "var2"),
Value: "var2",
},
},
Expand Down
225 changes: 110 additions & 115 deletions internal/lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,102 +16,101 @@ type Lexer struct {

/*
* Init a Lexer
*/
*/
func InitLexer(input string) *Lexer {
l := &Lexer{input: input}
l.readChar() // READ FIRST CHAR
l.readChar() // READ FIRST CHAR
return l
}


/*
* LEXER METHODS
*/
* LEXER METHODS
*/
func (l *Lexer) NextToken() token.Token {
// var tokens []token.Token
var test token.Token
l.ignoreWhiteSpace()

switch l.char{
case '=':
if l.peek() =='='{
prev :=l.char
l.readChar()
test = token.NewToken(token.EQUAL, string(prev)+ string(l.char));
} else {
test = token.NewToken(token.ASSIGN,string(l.char));
}
case '+':
if l.peek()=='+'{
prev := l.char
l.readChar()
test = token.NewToken(token.INCREMENT, string(prev)+ string(l.char))
}else{
test = token.NewToken(token.PLUS, string(l.char))
}
case '-':
if l.peek()=='-' {
prev :=l.char
l.readChar()
test = token.NewToken(token.DECREMENT,string(l.char)+string(prev))
}else{
test = token.NewToken(token.MINUS,string(l.char))
}
case '/':
test = token.NewToken(token.SLASH,string(l.char))
case '*':
test = token.NewToken(token.STAR,string(l.char))
case '!':
if l.peek() =='=' {
prev := l.char
l.readChar()
test = token.NewToken(token.NOT_EQUAL,string(prev)+ string(l.char))
}else {
test = token.NewToken(token.EX_MARK,string(l.char))
}
case '<':
if l.peek() =='='{
prev :=l.char
l.readChar()
test = token.NewToken(token.LT_OR_EQ, string(prev) + string(l.char))
}else{
test = token.NewToken(token.LT,string(l.char))
}
case '>':
if l.peek() == '='{
prev :=l.char
l.readChar()
test = token.NewToken(token.GT_OR_EQ,string(prev) + string(l.char))
}else{
test = token.NewToken(token.GT,string(l.char))
}
case ')':
test = token.NewToken(token.RP,string(l.char))
case '(':
test = token.NewToken(token.LP,string(l.char))
case '{':
test = token.NewToken(token.LCB, string(l.char))
case '}':
test = token.NewToken(token.RCB, string(l.char))
case ',':
test = token.NewToken(token.COMMA, string(l.char))
case ';':
test = token.NewToken(token.S_COLON, string(l.char))
case 0:
// program ends here
test = token.NewToken(token.FILE_ENDED, string(rune(0)))
default:
if utils.IsLetter(l.char) {
ident := l.ReadIdentifier()
test = token.NewToken(token.GetIdentifierTokenType(ident),ident)
return test
}else if utils.IsDigit(l.char){
num := l.ReadNumber()
test = token.NewToken(token.INT,num)
return test // this prevents calling read char which is already done with the method ReadNumber()
}else {
test = token.NewToken(token.ILLEGAL,string(l.char))
}
switch l.char {
case '=':
if l.peek() == '=' {
prev := l.char
l.readChar()
test = token.CreateToken(token.EQUAL, string(prev)+string(l.char))
} else {
test = token.CreateToken(token.ASSIGN, string(l.char))
}
case '+':
if l.peek() == '+' {
prev := l.char
l.readChar()
test = token.CreateToken(token.INCREMENT, string(prev)+string(l.char))
} else {
test = token.CreateToken(token.PLUS, string(l.char))
}
case '-':
if l.peek() == '-' {
prev := l.char
l.readChar()
test = token.CreateToken(token.DECREMENT, string(l.char)+string(prev))
} else {
test = token.CreateToken(token.MINUS, string(l.char))
}
case '/':
test = token.CreateToken(token.SLASH, string(l.char))
case '*':
test = token.CreateToken(token.STAR, string(l.char))
case '!':
if l.peek() == '=' {
prev := l.char
l.readChar()
test = token.CreateToken(token.NOT_EQUAL, string(prev)+string(l.char))
} else {
test = token.CreateToken(token.EX_MARK, string(l.char))
}
case '<':
if l.peek() == '=' {
prev := l.char
l.readChar()
test = token.CreateToken(token.LT_OR_EQ, string(prev)+string(l.char))
} else {
test = token.CreateToken(token.LT, string(l.char))
}
case '>':
if l.peek() == '=' {
prev := l.char
l.readChar()
test = token.CreateToken(token.GT_OR_EQ, string(prev)+string(l.char))
} else {
test = token.CreateToken(token.GT, string(l.char))
}
case ')':
test = token.CreateToken(token.RP, string(l.char))
case '(':
test = token.CreateToken(token.LP, string(l.char))
case '{':
test = token.CreateToken(token.LCB, string(l.char))
case '}':
test = token.CreateToken(token.RCB, string(l.char))
case ',':
test = token.CreateToken(token.COMMA, string(l.char))
case ';':
test = token.CreateToken(token.S_COLON, string(l.char))
case 0:
// program ends here
test = token.CreateToken(token.FILE_ENDED, string(rune(0)))
default:
if utils.IsLetter(l.char) {
ident := l.ReadIdentifier()
test = token.CreateToken(token.GetIdentifierTokenType(ident), ident)
return test
} else if utils.IsDigit(l.char) {
num := l.ReadNumber()
test = token.CreateToken(token.INT, num)
return test // this prevents calling read char which is already done with the method ReadNumber()
} else {
test = token.CreateToken(token.ILLEGAL, string(l.char))
}
}

l.readChar() // move to next char
Expand All @@ -121,57 +120,54 @@ func (l *Lexer) NextToken() token.Token {
// HELPER FUNCTIONS
/*
* This function give us the next character
*/
func (l *Lexer) readChar(){
if l.readPos >= utf8.RuneCount([]byte(l.input)){ // the number of runes in the string
*/
func (l *Lexer) readChar() {

if l.readPos >= utf8.RuneCount([]byte(l.input)) { // the number of runes in the string
l.char = 0 // SET THE CURRENT CHAR TO NUL CHARACTER (TO INDICATE THE TERMINATION OF THE STRING)
}else{
r, size :=utf8.DecodeRuneInString(l.input[l.readPos:])
} else {
r, size := utf8.DecodeRuneInString(l.input[l.readPos:])
l.char = r
l.currentPos = l.readPos;
l.currentPos = l.readPos
l.readPos += size
}
}
}

/*
* This function peeks the next character
* This function peeks the next character
* used in case of tokens that are compose of more than 2 tokens ( like "==" and "<=" ">=" and "!=")
*/
*/

func (l *Lexer) peek() rune {
// if we still in the input length range
if l.readPos >= utf8.RuneCount([]byte(l.input)) {
return 0;
return 0
}
return rune(l.input[l.readPos]);
return rune(l.input[l.readPos])
}




/*
* this functions reads the identifiers and keywords
* starting with a letter and stops when it finds a non letter character
*/
func (l *Lexer) ReadIdentifier() string{
currPosition := l.currentPos;
//identifiers can't start with numbers
if currPosition ==0 && utils.IsDigit(l.char){
return "" // this identifier is invalid
*/
func (l *Lexer) ReadIdentifier() string {
currPosition := l.currentPos

//identifiers can't start with numbers
if currPosition == 0 && utils.IsDigit(l.char) {
return "" // this identifier is invalid
}

for utils.IsDigit(l.char) || utils.IsLetter(l.char) {
for utils.IsDigit(l.char) || utils.IsLetter(l.char) {
l.readChar()
}
return l.input[currPosition:l.currentPos]
}

/*
* this function reads the numbers
* this function reads the numbers
* starting with a digit and stops when it reaches a non digit value
*/
*/
func (l *Lexer) ReadNumber() string {
currentPos := l.currentPos
for utils.IsDigit(l.char) {
Expand All @@ -180,12 +176,11 @@ func (l *Lexer) ReadNumber() string {
return l.input[currentPos:l.currentPos]
}


/*
* function to skip white space and break lines
*/
func (l *Lexer) ignoreWhiteSpace(){
for l.char ==' ' || l.char == '\t' || l.char == '\n' || l.char == '\r' {
*/
func (l *Lexer) ignoreWhiteSpace() {
for l.char == ' ' || l.char == '\t' || l.char == '\n' || l.char == '\r' {
l.readChar()
}
}
}
Loading

0 comments on commit 97fa1e4

Please sign in to comment.