diff --git a/gramatica/PortuscriptLexer.g4 b/gramatica/PortuscriptLexer.g4 index d92d238..badc4c4 100644 --- a/gramatica/PortuscriptLexer.g4 +++ b/gramatica/PortuscriptLexer.g4 @@ -61,6 +61,8 @@ NAO_BIT_A_BIT: '~'; DESLOC_ESQUERDA: '<<'; DESLOC_DIREITA: '>>'; PONTO: '.'; +ABRE_COLCHETES: '['; +FECHA_COLCHETES: ']'; ID: (LETRAS | '_') (LETRAS | DIGITOS)*; diff --git a/gramatica/PortuscriptParser.g4 b/gramatica/PortuscriptParser.g4 index 9e99fb1..1476cf2 100644 --- a/gramatica/PortuscriptParser.g4 +++ b/gramatica/PortuscriptParser.g4 @@ -78,4 +78,6 @@ argumento: expressao; atomo: ID | 'Verdadeiro' | 'Falso' | 'Nulo' | TEXTO+ | DIGITOS | tupla; -tupla: ABRE_CHAVES expressao VIRGULA (expressao VIRGULA?)* FECHA_CHAVES; \ No newline at end of file +tupla: ABRE_CHAVES expressao VIRGULA (expressao VIRGULA?)* FECHA_CHAVES; + +lista: ABRE_COLCHETES expressao (VIRGULA expressao)* FECHA_COLCHETES; \ No newline at end of file diff --git a/lexer/lexer.go b/lexer/lexer.go index 1b5c523..ed0e5e9 100644 --- a/lexer/lexer.go +++ b/lexer/lexer.go @@ -215,6 +215,12 @@ func (l *Lexer) ProximoToken() Token { case "}": l.avancar() return Token{TokenFechaChaves, "}"} + case "[": + l.avancar() + return Token{TokenAbreColchetes, "["} + case "]": + l.avancar() + return Token{TokenFechaColchetes, "]"} case "|": l.avancar() return Token{TokenBitABitOu, "|"} diff --git a/lexer/tokens.go b/lexer/tokens.go index ef1602c..9bb3a8b 100644 --- a/lexer/tokens.go +++ b/lexer/tokens.go @@ -49,6 +49,8 @@ const ( TokenVirgula // , TokenAbreChaves // { TokenFechaChaves // } + TokenAbreColchetes // { + TokenFechaColchetes // } TokenDoisPontos // : // Reatribuicao diff --git a/parser/ast_nodes.go b/parser/ast_nodes.go index 37d2c43..aaddf50 100644 --- a/parser/ast_nodes.go +++ b/parser/ast_nodes.go @@ -113,6 +113,10 @@ type TuplaLiteral struct { Elementos []BaseNode } +type ListaLiteral struct { + Elementos []BaseNode +} + func (*Programa) isExpr() {} func (*DeclVar) isExpr() {} func (*Reatribuicao) isExpr() {} @@ -136,3 +140,4 @@ func (*BlocoPara) isExpr() {} func (*PareNode) isExpr() {} func (*ContinueNode) isExpr() {} func (*TuplaLiteral) isExpr() {} +func (*ListaLiteral) isExpr() {} diff --git a/parser/parser.go b/parser/parser.go index b53dd6a..a7b12c5 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -756,6 +756,22 @@ func (p *Parser) parseAtomo() (BaseNode, error) { literal.Elementos = append(literal.Elementos, exp) } + p.avancar() + return literal, nil + case lexer.TokenAbreColchetes: + literal := &ListaLiteral{} + + for p.token.Tipo != lexer.TokenFechaColchetes { + p.avancar() + exp, err := p.parseExpressao() + + if err != nil { + return nil, err + } + + literal.Elementos = append(literal.Elementos, exp) + } + p.avancar() return literal, nil } diff --git a/ptst/erros.go b/ptst/erros.go index e167d71..e736597 100644 --- a/ptst/erros.go +++ b/ptst/erros.go @@ -21,6 +21,8 @@ var ( TipagemErro = TipoErro.NewTipo("TipagemErro", "Tipo de argumento inapropriado.") NomeErro = TipoErro.NewTipo("NomeErro", "Erro de nome que não pode ser achado.") ImportacaoErro = TipoErro.NewTipo("ImportacaoErro", "Não é possível encontrar o módulo ou símbolo nele") + ValorErro = TipoErro.NewTipo("ValorErro", "O valor é inapropriádo ou sua ocorrencia não existe") + IndiceErro = TipoErro.NewTipo("IndiceErro", "O indice está fora do range aceito") FimIteracao = TipoErro.NewTipo("FimIteracao", "Sinaliza o fim da iteração quando `objeto.__proximo__() não retorna mais nada") // Apenas para fins de controle, não são necessariamente erros diff --git a/ptst/interpretador.go b/ptst/interpretador.go index d041502..38d4f5f 100644 --- a/ptst/interpretador.go +++ b/ptst/interpretador.go @@ -63,6 +63,8 @@ func (i *Interpretador) visite(node parser.BaseNode) (Objeto, error) { return i.visiteDecimalLiteral(node.(*parser.DecimalLiteral)) case *parser.TuplaLiteral: return i.visiteTuplaLiteral(node.(*parser.TuplaLiteral)) + case *parser.ListaLiteral: + return i.visiteListaLiteral(node.(*parser.ListaLiteral)) case *parser.OpBinaria: return i.visiteOpBinaria(node.(*parser.OpBinaria)) case *parser.Identificador: @@ -193,6 +195,20 @@ func (i *Interpretador) visiteTuplaLiteral(node *parser.TuplaLiteral) (Objeto, e return tupla, nil } +func (i *Interpretador) visiteListaLiteral(node *parser.ListaLiteral) (Objeto, error) { + lista := &Lista{} + + for _, elemento := range node.Elementos { + item, err := i.visite(elemento) + if err != nil { + return nil, err + } + + lista.Adiciona(item) + } + return lista, nil +} + func (i *Interpretador) visiteOpBinaria(node *parser.OpBinaria) (Objeto, error) { esquerda, err := i.visite(node.Esq) @@ -402,7 +418,7 @@ func (i *Interpretador) visiteBlocoPara(node *parser.BlocoPara) (Objeto, error) return nil, err } - } + } } func (i *Interpretador) visitePareNode(node *parser.PareNode) (Objeto, error) { diff --git a/ptst/lista.go b/ptst/lista.go new file mode 100644 index 0000000..2d38ba6 --- /dev/null +++ b/ptst/lista.go @@ -0,0 +1,143 @@ +package ptst + +type Lista struct { + Itens Tupla +} + +var TipoLista = TipoObjeto.NewTipo( + "Lista", + "Lista(obj) -> Lista", +) + +func (l *Lista) Tipo() *Tipo { + return TipoLista +} + +func (l *Lista) O__iter__() (Objeto, error) { + return NewIterador(l.Itens) +} + +func (l *Lista) O__texto__() (Objeto, error) { + return l.Itens.GRepr("[", "]") +} + +func (l *Lista) O__tamanho__() (Objeto, error) { + return l.Itens.O__tamanho__() +} + +var _ I__iter__ = (*Lista)(nil) +var _ I__texto__ = (*Lista)(nil) +var _ I__tamanho__ = (*Lista)(nil) + +func (l *Lista) Adiciona(item Objeto) (Objeto, error) { + l.Itens = append(l.Itens, item) + return nil, nil +} + +func (l *Lista) Indice(obj Objeto) (Objeto, error) { + for indice, item := range l.Itens { + if ok, _ := Igual(item, obj); ok.(Booleano) { + return Inteiro(indice), nil + } + } + + objTexto, err := NewTexto(obj) + if err != nil { + return nil, err + } + + return nil, NewErroF(ValorErro, "O item '%s' não está na lista", objTexto) +} + +func (l *Lista) Pop(indice Inteiro) (Objeto, error) { + tamanho, err := l.O__tamanho__() + if err != nil { + return nil, err + } + + if indice > tamanho.(Inteiro) || indice < 0 { + return nil, NewErroF(IndiceErro, "O range é de %d indice(s), %d está fora dele", tamanho.(Inteiro), indice) + } + + var removido Objeto + var novaTupla Tupla + + for idx, item := range l.Itens { + if idx == int(indice) { + removido = item + continue + } + + novaTupla = append(novaTupla, item) + } + + l.Itens = novaTupla + return removido, nil +} + +func init() { + TipoLista.Mapa["adiciona"] = NewMetodoOuPanic("adiciona", func(inst Objeto, args Tupla) (Objeto, error) { + if len(args) < 1 { + return nil, NewErroF(TipagemErro, "O método adiciona() esperava receber no mínimo 1 argumento, mas recebeu um total de %v", len(args)) + } + + inst.(*Lista).Adiciona(args[0]) + return nil, nil + }, "O método recebe um objeto e adiciona ao fim da lista") + + // TipoLista.Mapa["insere"] = NewMetodoOuPanic("insere", func(inst Objeto, args Tupla) (Objeto, error) { + // if len(args) < 2 { + // return nil, NewErroF(TipagemErro, "O método insere() esperava receber no mínimo 2 argumentos, mas recebeu um total de %v", len(args)) + // } + + // indice, objeto := args[0], args[1] + + // return nil, nil + // }, "") + + TipoLista.Mapa["extende"] = NewMetodoOuPanic("extende", func(inst Objeto, args Tupla) (Objeto, error) { + if len(args) < 1 { + return nil, NewErroF(TipagemErro, "O método extende() esperava receber no mínimo 1 argumento, mas recebeu um total de %v", len(args)) + } + + inst.(*Lista).Itens = append(inst.(*Lista).Itens, (args[0].(Tupla))...) + return nil, nil + }, "Adiciona os elementos da lista recebida ao fim da lista atual") + + TipoLista.Mapa["remove"] = NewMetodoOuPanic("remove", func(inst Objeto, args Tupla) (Objeto, error) { + if len(args) < 1 { + return nil, NewErroF(TipagemErro, "O método remove() esperava receber no mínimo 1 argumento, mas recebeu um total de %v", len(args)) + } + + instancia := inst.(*Lista) + idx, err := instancia.Indice(args[0]) + if err != nil { + return nil, err + } + + return instancia.Pop(idx.(Inteiro)) + }, "Remove um elemento da lista e o retorna, se existir") + + TipoLista.Mapa["pop"] = NewMetodoOuPanic("pop", func(inst Objeto, args Tupla) (Objeto, error) { + idx := Inteiro(0) + + if len(args) == 1 { + idx = args[0].(Inteiro) + } + + return inst.(*Lista).Pop(idx) + }, "Remove um item da lista com base no seu índice") + + TipoLista.Mapa["indice"] = NewMetodoOuPanic("indice", func(inst Objeto, args Tupla) (Objeto, error) { + if len(args) < 1 { + return nil, NewErroF(TipagemErro, "O método indice() esperava receber no mínimo 1 argumento, mas recebeu um total de %v", len(args)) + } + + return inst.(*Lista).Indice(args[0]) + }, "Retorna o índice de um elemento se ele existir na lista") + + TipoLista.Mapa["limpa"] = NewMetodoOuPanic("limpa", func(inst Objeto) (Objeto, error) { + inst.(*Lista).Itens = Tupla(nil) + return nil, nil + }, "Limpa completamente a lista") +} \ No newline at end of file